• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

knowledgepixels / nanodash / 19368829457

14 Nov 2025 03:12PM UTC coverage: 14.107% (+0.4%) from 13.676%
19368829457

push

github

tkuhn
feat(SpaceUserList): Show source nanopubs for Space memberships

535 of 4786 branches covered (11.18%)

Branch coverage included in aggregate %.

1385 of 8824 relevant lines covered (15.7%)

0.7 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

31.64
src/main/java/com/knowledgepixels/nanodash/Space.java
1
package com.knowledgepixels.nanodash;
2

3
import static com.knowledgepixels.nanodash.Utils.vf;
4

5
import java.io.Serializable;
6
import java.util.ArrayList;
7
import java.util.Calendar;
8
import java.util.Collections;
9
import java.util.HashMap;
10
import java.util.HashSet;
11
import java.util.List;
12
import java.util.Map;
13
import java.util.Set;
14

15
import org.apache.commons.lang3.tuple.Pair;
16
import org.eclipse.rdf4j.model.IRI;
17
import org.eclipse.rdf4j.model.Literal;
18
import org.eclipse.rdf4j.model.Statement;
19
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
20
import org.eclipse.rdf4j.model.vocabulary.OWL;
21
import org.nanopub.Nanopub;
22
import org.nanopub.extra.services.ApiResponse;
23
import org.nanopub.extra.services.ApiResponseEntry;
24
import org.nanopub.extra.services.QueryRef;
25
import org.nanopub.vocabulary.NTEMPLATE;
26
import org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28

29
import com.github.jsonldjava.shaded.com.google.common.collect.Ordering;
30
import com.google.common.collect.ArrayListMultimap;
31
import com.google.common.collect.Multimap;
32
import com.knowledgepixels.nanodash.template.Template;
33
import com.knowledgepixels.nanodash.template.TemplateData;
34

35
import jakarta.xml.bind.DatatypeConverter;
36

37
/**
38
 * Class representing a "Space", which can be any kind of collaborative unit, like a project, group, or event.
39
 */
40
public class Space implements Serializable {
41

42
    private static final Logger logger = LoggerFactory.getLogger(Space.class);
3✔
43

44
    /**
45
     * The predicate to assign the admins of the space.
46
     */
47
    public static final IRI HAS_ADMIN = vf.createIRI("https://w3id.org/kpxl/gen/terms/hasAdmin");
4✔
48

49
    /**
50
     * The predicate for pinned templates in the space.
51
     */
52
    public static final IRI HAS_PINNED_TEMPLATE = vf.createIRI("https://w3id.org/kpxl/gen/terms/hasPinnedTemplate");
4✔
53

54
    /**
55
     * The predicate for pinned queries in the space.
56
     */
57
    public static final IRI HAS_PINNED_QUERY = vf.createIRI("https://w3id.org/kpxl/gen/terms/hasPinnedQuery");
4✔
58

59
    private static List<Space> spaceList;
60
    private static Map<String, List<Space>> spaceListByType;
61
    private static Map<String, Space> spacesByCoreInfo = new HashMap<>();
4✔
62
    private static Map<String, Space> spacesById;
63
    private static Map<Space, Set<Space>> subspaceMap;
64
    private static Map<Space, Set<Space>> superspaceMap;
65
    private static boolean loaded = false;
2✔
66
    private static Long runRootUpdateAfter = null;
3✔
67

68
    /**
69
     * Refresh the list of spaces from the API response.
70
     *
71
     * @param resp The API response containing space data.
72
     */
73
    public static synchronized void refresh(ApiResponse resp) {
74
        spaceList = new ArrayList<>();
4✔
75
        spaceListByType = new HashMap<>();
4✔
76
        Map<String, Space> prevSpacesByCoreInfoPrev = spacesByCoreInfo;
2✔
77
        spacesByCoreInfo = new HashMap<>();
4✔
78
        spacesById = new HashMap<>();
4✔
79
        subspaceMap = new HashMap<>();
4✔
80
        superspaceMap = new HashMap<>();
4✔
81
        for (ApiResponseEntry entry : resp.getData()) {
11✔
82
            Space space = new Space(entry);
5✔
83
            Space prevSpace = prevSpacesByCoreInfoPrev.get(space.getCoreInfoString());
6✔
84
            if (prevSpace != null) space = prevSpace;
4✔
85
            spaceList.add(space);
4✔
86
            spaceListByType.computeIfAbsent(space.getType(), k -> new ArrayList<>()).add(space);
13✔
87
            spacesByCoreInfo.put(space.getCoreInfoString(), space);
6✔
88
            spacesById.put(space.getId(), space);
6✔
89
        }
1✔
90
        for (Space space : spaceList) {
10✔
91
            Space superSpace = space.getIdSuperspace();
3✔
92
            if (superSpace == null) continue;
3✔
93
            subspaceMap.computeIfAbsent(superSpace, k -> new HashSet<>()).add(space);
12✔
94
            superspaceMap.computeIfAbsent(space, k -> new HashSet<>()).add(superSpace);
12✔
95
        }
1✔
96
        loaded = true;
2✔
97
    }
1✔
98

99
    /**
100
     * Check if the spaces have been loaded.
101
     *
102
     * @return true if loaded, false otherwise.
103
     */
104
    public static boolean isLoaded() {
105
        return loaded;
×
106
    }
107

108
    public static boolean areAllSpacesInitialized() {
109
        for (Space space : spaceList) {
×
110
            if (!space.isDataInitialized()) return false;
×
111
        }
×
112
        return true;
×
113
    }
114

115
    public static void triggerAllDataUpdates() {
116
        for (Space space : spaceList) {
×
117
            space.triggerDataUpdate();
×
118
        }
×
119
    }
×
120

121
    /**
122
     * Ensure that the spaces are loaded, fetching them from the API if necessary.
123
     */
124
    public static void ensureLoaded() {
125
        if (spaceList == null) {
2✔
126
            try {
127
                if (runRootUpdateAfter != null) {
2!
128
                    while (System.currentTimeMillis() < runRootUpdateAfter) {
×
129
                        Thread.sleep(100);
×
130
                    }
131
                    runRootUpdateAfter = null;
×
132
                }
133
            } catch (InterruptedException ex) {
×
134
                logger.error("Interrupted", ex);
×
135
            }
1✔
136
            refresh(QueryApiAccess.forcedGet(new QueryRef("get-spaces")));
6✔
137
        }
138
    }
1✔
139

140
    public static void forceRootRefresh(long waitMillis) {
141
        spaceList = null;
×
142
        runRootUpdateAfter = System.currentTimeMillis() + waitMillis;
×
143
    }
×
144

145
    /**
146
     * Get the list of all spaces.
147
     *
148
     * @return List of spaces.
149
     */
150
    public static List<Space> getSpaceList() {
151
        ensureLoaded();
×
152
        return spaceList;
×
153
    }
154

155
    /**
156
     * Get the list of spaces of a specific type.
157
     *
158
     * @param type The type of spaces to retrieve.
159
        System.err.println("REFRESH...");
160
     * @return List of spaces of the specified type.
161
     */
162
    public static List<Space> getSpaceList(String type) {
163
        ensureLoaded();
×
164
        return spaceListByType.computeIfAbsent(type, k -> new ArrayList<>());
×
165
    }
166

167
    /**
168
     * Get a space by its id.
169
     *
170
     * @param id The id of the space.
171
     * @return The corresponding Space object, or null if not found.
172
     */
173
    public static Space get(String id) {
174
        ensureLoaded();
1✔
175
        return spacesById.get(id);
5✔
176
    }
177

178
    /**
179
     * Mark all spaces as needing a data update.
180
     */
181
    public static void refresh() {
182
        refresh(QueryApiAccess.forcedGet(new QueryRef("get-spaces")));
6✔
183
        for (Space space : spaceList) {
10✔
184
            space.dataNeedsUpdate = true;
3✔
185
        }
1✔
186
    }
1✔
187

188
    public void forceRefresh(long waitMillis) {
189
        dataNeedsUpdate = true;
×
190
        dataInitialized = false;
×
191
        runUpdateAfter = System.currentTimeMillis() + waitMillis;
×
192
    }
×
193

194
    private String id, label, rootNanopubId, type;
195
    private Nanopub rootNanopub = null;
3✔
196
    private SpaceData data = new SpaceData();
5✔
197

198
    private static class SpaceData implements Serializable {
2✔
199

200
        List<String> altIds = new ArrayList<>();
5✔
201

202
        String description = null;
3✔
203
        Calendar startDate, endDate;
204
        IRI defaultProvenance = null;
3✔
205

206
        List<IRI> admins = new ArrayList<>();
5✔
207
        // TODO Make Pair<SpaceMemberRole, String> a new class with SpaceMemberRole + nanopub URI
208
        Map<IRI, Set<Pair<SpaceMemberRole, String>>> users = new HashMap<>();
5✔
209
        List<SpaceMemberRole> roles = new ArrayList<>();
5✔
210
        Map<IRI, SpaceMemberRole> roleMap = new HashMap<>();
5✔
211

212
        Map<String, IRI> adminPubkeyMap = new HashMap<>();
5✔
213
        Set<Serializable> pinnedResources = new HashSet<>();
5✔
214
        List<ViewDisplay> topLevelViews = new ArrayList<>();
5✔
215
        List<ViewDisplay> partLevelViews = new ArrayList<>();
5✔
216
        Set<String> pinGroupTags = new HashSet<>();
5✔
217
        Map<String, Set<Serializable>> pinnedResourceMap = new HashMap<>();
6✔
218

219
        void addAdmin(IRI admin, String npId) {
220
            // TODO This isn't efficient for long owner lists:
221
            if (admins.contains(admin)) return;
5!
222
            admins.add(admin);
5✔
223
            UserData ud = User.getUserData();
2✔
224
            for (String pubkeyhash : ud.getPubkeyhashes(admin, true)) {
14✔
225
                adminPubkeyMap.put(pubkeyhash, admin);
6✔
226
            }
1✔
227
            users.computeIfAbsent(admin, (k) -> new HashSet<>()).add(Pair.of(SpaceMemberRole.ADMIN_ROLE, npId));
15✔
228
        }
1✔
229

230
    }
231

232
    private boolean dataInitialized = false;
3✔
233
    private boolean dataNeedsUpdate = true;
3✔
234
    private Long runUpdateAfter = null;
3✔
235

236
    private Space(ApiResponseEntry resp) {
2✔
237
        this.id = resp.get("space");
5✔
238
        this.label = resp.get("label");
5✔
239
        this.type = resp.get("type");
5✔
240
        this.rootNanopubId = resp.get("np");
5✔
241
        this.rootNanopub = Utils.getAsNanopub(rootNanopubId);
5✔
242
        setCoreData(data);
4✔
243
    }
1✔
244

245
    /**
246
     * Get the ID of the space.
247
     *
248
     * @return The space ID.
249
     */
250
    public String getId() {
251
        return id;
3✔
252
    }
253

254
    /**
255
     * Get the root nanopublication ID of the space.
256
     *
257
     * @return The root nanopub ID.
258
     */
259
    public String getRootNanopubId() {
260
        return rootNanopubId;
×
261
    }
262

263
    /**
264
     * Get a string combining the space ID and root nanopub ID for core identification.
265
     *
266
     * @return The core info string.
267
     */
268
    public String getCoreInfoString() {
269
        return id + " " + rootNanopubId;
6✔
270
    }
271

272
    /**
273
     * Get the root nanopublication of the space.
274
     *
275
     * @return The root Nanopub object.
276
     */
277
    public Nanopub getRootNanopub() {
278
        return rootNanopub;
×
279
    }
280

281
    /**
282
     * Get the label of the space.
283
     *
284
     * @return The space label.
285
     */
286
    public String getLabel() {
287
        return label;
×
288
    }
289

290
    /**
291
     * Get the type of the space.
292
     *
293
     * @return The space type.
294
     */
295
    public String getType() {
296
        return type;
3✔
297
    }
298

299
    /**
300
     * Get the start date of the space.
301
     *
302
     * @return The start date as a Calendar object, or null if not set.
303
     */
304
    public Calendar getStartDate() {
305
        return data.startDate;
×
306
    }
307

308
    /**
309
     * Get the end date of the space.
310
     *
311
     * @return The end date as a Calendar object, or null if not set.
312
     */
313
    public Calendar getEndDate() {
314
        return data.endDate;
×
315
    }
316

317
    /**
318
     * Get a simplified label for the type of space by removing any namespace prefix.
319
     *
320
     * @return The simplified type label.
321
     */
322
    public String getTypeLabel() {
323
        return type.replaceFirst("^.*/", "");
×
324
    }
325

326
    /**
327
     * Get the description of the space.
328
     *
329
     * @return The description string.
330
     */
331
    public String getDescription() {
332
        return data.description;
×
333
    }
334

335
    /**
336
     * Check if the space data has been initialized.
337
     *
338
     * @return true if initialized, false otherwise.
339
     */
340
    public boolean isDataInitialized() {
341
        triggerDataUpdate();
×
342
        return dataInitialized;
×
343
    }
344

345
    /**
346
     * Get the list of admins in this space.
347
     *
348
     * @return List of admin IRIs.
349
     */
350
    public List<IRI> getAdmins() {
351
        ensureInitialized();
×
352
        return data.admins;
×
353
    }
354

355
    /**
356
     * Get the list of members in this space.
357
     *
358
     * @return List of member IRIs.
359
     */
360
    public List<IRI> getUsers() {
361
        ensureInitialized();
×
362
        List<IRI> users = new ArrayList<IRI>(data.users.keySet());
×
363
        users.sort(User.getUserData().userComparator);
×
364
        return users;
×
365
    }
366

367
    /**
368
     * Get the roles of a specific member in this space.
369
     *
370
     * @param userId The IRI of the member.
371
     * @return Set of roles assigned to the member, or null if the member is not part of this space.
372
     */
373
    public Set<Pair<SpaceMemberRole,String>> getMemberRoles(IRI userId) {
374
        ensureInitialized();
×
375
        return data.users.get(userId);
×
376
    }
377

378
    /**
379
     * Check if a user is a member of this space.
380
     *
381
     * @param userId The IRI of the user to check.
382
     * @return true if the user is a member, false otherwise.
383
     */
384
    public boolean isMember(IRI userId) {
385
        ensureInitialized();
×
386
        return data.users.containsKey(userId);
×
387
    }
388

389
    public boolean isAdminPubkey(String pubkey) {
390
        ensureInitialized();
×
391
        return data.adminPubkeyMap.containsKey(pubkey);
×
392
    }
393

394
    /**
395
     * Get the list of pinned resources in this space.
396
     *
397
     * @return List of pinned resources.
398
     */
399
    public Set<Serializable> getPinnedResources() {
400
        ensureInitialized();
×
401
        return data.pinnedResources;
×
402
    }
403

404
    /**
405
     * Get the set of tags used for grouping pinned resources.
406
     *
407
     * @return Set of tags.
408
     */
409
    public Set<String> getPinGroupTags() {
410
        ensureInitialized();
×
411
        return data.pinGroupTags;
×
412
    }
413

414
    /**
415
     * Get a map of pinned resources grouped by their tags.
416
     *
417
     * @return Map where keys are tags and values are lists of pinned resources (Templates or GrlcQueries).
418
     */
419
    public Map<String, Set<Serializable>> getPinnedResourceMap() {
420
        ensureInitialized();
×
421
        return data.pinnedResourceMap;
×
422
    }
423

424
    /**
425
     * Returns the view displays and their associated nanopub IDs.
426
     *
427
     * @return Map of views to nanopub IDs
428
     */
429
    public List<ViewDisplay> getTopLevelViews() {
430
        return data.topLevelViews;
×
431
    }
432

433
    public List<ViewDisplay> getPartLevelViews(Set<IRI> classes) {
434
        triggerDataUpdate();
×
435
        List<ViewDisplay> viewDisplays = new ArrayList<>();
×
436
        for (ViewDisplay v : data.partLevelViews) {
×
437
            if (v.getView().hasTargetClasses()) {
×
438
                for (IRI c : classes) {
×
439
                    if (v.getView().hasTargetClass(c)) {
×
440
                        viewDisplays.add(v);
×
441
                        break;
×
442
                    }
443
                }
×
444
            } else {
445
                viewDisplays.add(v);
×
446
            }
447
        }
×
448
        return viewDisplays;
×
449
    }
450

451
    public boolean coversElement(String elementId) {
452
        triggerDataUpdate();
×
453
        for (ViewDisplay v : data.topLevelViews) {
×
454
            if (v.getView().coversElement(elementId)) return true;
×
455
        }
×
456
        for (ViewDisplay v : data.partLevelViews) {
×
457
            if (v.getView().coversElement(elementId)) return true;
×
458
        }
×
459
        return false;
×
460
    }
461

462
    /**
463
     * Get the default provenance IRI for this space.
464
     *
465
     * @return The default provenance IRI, or null if not set.
466
     */
467
    public IRI getDefaultProvenance() {
468
        return data.defaultProvenance;
×
469
    }
470

471
    /**
472
     * Get the roles defined in this space.
473
     *
474
     * @return List of roles.
475
     */
476
    public List<SpaceMemberRole> getRoles() {
477
        return data.roles;
×
478
    }
479

480
    /**
481
     * Get the super ID of the space.
482
     *
483
     * @return Always returns null. Use getIdSuperspace() instead.
484
     */
485
    public String getSuperId() {
486
        return null;
×
487
    }
488

489
    /**
490
     * Get the superspace ID.
491
     *
492
     * @return The superspace, or null if not applicable.
493
     */
494
    public Space getIdSuperspace() {
495
        if (!id.matches("https?://[^/]+/.*/[^/]*/?")) return null;
5!
496
        String superId = id.replaceFirst("(https?://[^/]+/.*)/[^/]*/?", "$1");
6✔
497
        if (spacesById.containsKey(superId)) {
4✔
498
            return spacesById.get(superId);
5✔
499
        }
500
        return null;
2✔
501
    }
502

503
    /**
504
     * Get superspaces of this space.
505
     *
506
     * @return List of superspaces.
507
     */
508
    public List<Space> getSuperspaces() {
509
        if (superspaceMap.containsKey(this)) {
×
510
            List<Space> superspaces = new ArrayList<>(superspaceMap.get(this));
×
511
            Collections.sort(superspaces, Ordering.usingToString());
×
512
            return superspaces;
×
513
        }
514
        return new ArrayList<>();
×
515
    }
516

517
    /**
518
     * Get subspaces of this space.
519
     *
520
     * @return List of subspaces.
521
     */
522
    public List<Space> getSubspaces() {
523
        if (subspaceMap.containsKey(this)) {
×
524
            List<Space> subspaces = new ArrayList<>(subspaceMap.get(this));
×
525
            Collections.sort(subspaces, Ordering.usingToString());
×
526
            return subspaces;
×
527
        }
528
        return new ArrayList<>();
×
529
    }
530

531
    /**
532
     * Get subspaces of a specific type.
533
     *
534
     * @param type The type of subspaces to retrieve.
535
     * @return List of subspaces of the specified type.
536
     */
537
    public List<Space> getSubspaces(String type) {
538
        List<Space> l = new ArrayList<>();
×
539
        for (Space s : getSubspaces()) {
×
540
            if (s.getType().equals(type)) l.add(s);
×
541
        }
×
542
        return l;
×
543
    }
544

545
    /**
546
     * Get alternative IDs for the space.
547
     *
548
     * @return List of alternative IDs.
549
     */
550
    public List<String> getAltIDs() {
551
        return data.altIds;
×
552
    }
553

554
    private synchronized void ensureInitialized() {
555
        Thread thread = triggerDataUpdate();
×
556
        if (!dataInitialized && thread != null) {
×
557
            try {
558
                thread.join();
×
559
            } catch (InterruptedException ex) {
×
560
                logger.error("failed to join thread", ex);
×
561
            }
×
562
        }
563
    }
×
564

565
    private synchronized Thread triggerDataUpdate() {
566
        if (dataNeedsUpdate) {
×
567
            Thread thread = new Thread(() -> {
×
568
                try {
569
                    if (runUpdateAfter != null) {
×
570
                        while (System.currentTimeMillis() < runUpdateAfter) {
×
571
                            Thread.sleep(100);
×
572
                        }
573
                        runUpdateAfter = null;
×
574
                    }
575
                    SpaceData newData = new SpaceData();
×
576
                    setCoreData(newData);
×
577

578
                    newData.roles.add(SpaceMemberRole.ADMIN_ROLE);
×
579
                    newData.roleMap.put(SpaceMemberRole.HAS_ADMIN_PREDICATE, SpaceMemberRole.ADMIN_ROLE);
×
580

581
                    // TODO Improve this:
582
                    Multimap<String, String> spaceIds = ArrayListMultimap.create();
×
583
                    Multimap<String, String> resourceIds = ArrayListMultimap.create();
×
584
                    spaceIds.put("space", id);
×
585
                    resourceIds.put("resource", id);
×
586
                    for (String id : newData.altIds) {
×
587
                        spaceIds.put("space", id);
×
588
                        resourceIds.put("resource", id);
×
589
                    }
×
590

591
                    ApiResponse getAdminsResponse = QueryApiAccess.get(new QueryRef("get-admins", spaceIds));
×
592
                    boolean continueAddingAdmins = true;
×
593
                    while (continueAddingAdmins) {
×
594
                        continueAddingAdmins = false;
×
595
                        for (ApiResponseEntry r : getAdminsResponse.getData()) {
×
596
                            String pubkeyhash = r.get("pubkey");
×
597
                            if (newData.adminPubkeyMap.containsKey(pubkeyhash)) {
×
598
                                IRI adminId = Utils.vf.createIRI(r.get("admin"));
×
599
                                if (!newData.admins.contains(adminId)) {
×
600
                                    continueAddingAdmins = true;
×
601
                                    newData.addAdmin(adminId, r.get("np"));
×
602
                                }
603
                            }
604
                        }
×
605
                    }
606
                    newData.admins.sort(User.getUserData().userComparator);
×
607

608
                    Multimap<String, String> getSpaceMemberParams = ArrayListMultimap.create(spaceIds);
×
609

610
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-space-member-roles", spaceIds)).getData()) {
×
611
                        if (!newData.adminPubkeyMap.containsKey(r.get("pubkey"))) continue;
×
612
                        SpaceMemberRole role = new SpaceMemberRole(r);
×
613
                        newData.roles.add(role);
×
614

615
                        // TODO Handle cases of overlapping properties:
616
                        for (IRI p : role.getRegularProperties()) newData.roleMap.put(p, role);
×
617
                        for (IRI p : role.getInverseProperties()) newData.roleMap.put(p, role);
×
618

619
                        role.addRoleParams(getSpaceMemberParams);
×
620
                    }
×
621

622
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-space-members", getSpaceMemberParams)).getData()) {
×
623
                        IRI memberId = Utils.vf.createIRI(r.get("member"));
×
624
                        SpaceMemberRole role = newData.roleMap.get(Utils.vf.createIRI(r.get("role")));
×
625
                        newData.users.computeIfAbsent(memberId, (k) -> new HashSet<>()).add(Pair.of(role, r.get("np")));
×
626
                    }
×
627

628
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-pinned-templates", spaceIds)).getData()) {
×
629
                        if (!newData.adminPubkeyMap.containsKey(r.get("pubkey"))) continue;
×
630
                        Template t = TemplateData.get().getTemplate(r.get("template"));
×
631
                        if (t == null) continue;
×
632
                        newData.pinnedResources.add(t);
×
633
                        String tag = r.get("tag");
×
634
                        if (tag != null && !tag.isEmpty()) {
×
635
                            newData.pinGroupTags.add(r.get("tag"));
×
636
                            newData.pinnedResourceMap.computeIfAbsent(tag, k -> new HashSet<>()).add(TemplateData.get().getTemplate(r.get("template")));
×
637
                        }
638
                    }
×
639
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-pinned-queries", spaceIds)).getData()) {
×
640
                        if (!newData.adminPubkeyMap.containsKey(r.get("pubkey"))) continue;
×
641
                        GrlcQuery query = GrlcQuery.get(r.get("query"));
×
642
                        if (query == null) continue;
×
643
                        newData.pinnedResources.add(query);
×
644
                        String tag = r.get("tag");
×
645
                        if (tag != null && !tag.isEmpty()) {
×
646
                            newData.pinGroupTags.add(r.get("tag"));
×
647
                            newData.pinnedResourceMap.computeIfAbsent(tag, k -> new HashSet<>()).add(query);
×
648
                        }
649
                    }
×
650
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-view-displays", resourceIds)).getData()) {
×
651
                        if (!newData.adminPubkeyMap.containsKey(r.get("pubkey"))) continue;
×
652
                        try {
653
                            ViewDisplay vd = ViewDisplay.get(r.get("display"));
×
654
                            if (ResourceView.PART_LEVEL_VIEW_DISPLAY.stringValue().equals(r.get("displayType"))) {
×
655
                                newData.partLevelViews.add(vd);
×
656
                            } else {
657
                                newData.topLevelViews.add(vd);
×
658
                            }
659
                        } catch (IllegalArgumentException ex) {
×
660
                            logger.error("Couldn't generate view display object", ex);
×
661
                        }
×
662
                    }
×
663
                    Collections.sort(newData.topLevelViews);
×
664
                    Collections.sort(newData.partLevelViews);
×
665
                    data = newData;
×
666
                    dataInitialized = true;
×
667
                } catch (Exception ex) {
×
668
                    logger.error("Error while trying to update space data: {}", ex);
×
669
                }
×
670
            });
×
671
            thread.start();
×
672
            dataNeedsUpdate = false;
×
673
            return thread;
×
674
        }
675
        return null;
×
676
    }
677

678
    private void setCoreData(SpaceData data) {
679
        for (Statement st : rootNanopub.getAssertion()) {
12✔
680
            if (st.getSubject().stringValue().equals(getId())) {
7!
681
                if (st.getPredicate().equals(OWL.SAMEAS) && st.getObject() instanceof IRI objIri) {
14!
682
                    data.altIds.add(objIri.stringValue());
7✔
683
                } else if (st.getPredicate().equals(DCTERMS.DESCRIPTION)) {
5✔
684
                    data.description = st.getObject().stringValue();
6✔
685
                } else if (st.getPredicate().stringValue().equals("http://schema.org/startDate")) {
6✔
686
                    try {
687
                        data.startDate = DatatypeConverter.parseDateTime(st.getObject().stringValue());
6✔
688
                    } catch (IllegalArgumentException ex) {
×
689
                        logger.error("Failed to parse date {}", st.getObject().stringValue());
×
690
                    }
1✔
691
                } else if (st.getPredicate().stringValue().equals("http://schema.org/endDate")) {
6✔
692
                    try {
693
                        data.endDate = DatatypeConverter.parseDateTime(st.getObject().stringValue());
6✔
694
                    } catch (IllegalArgumentException ex) {
×
695
                        logger.error("Failed to parse date {}", st.getObject().stringValue());
×
696
                    }
1✔
697
                } else if (st.getPredicate().equals(HAS_ADMIN) && st.getObject() instanceof IRI obj) {
14!
698
                    data.addAdmin(obj, rootNanopub.getUri().stringValue());
8✔
699
                } else if (st.getPredicate().equals(HAS_PINNED_TEMPLATE) && st.getObject() instanceof IRI obj) {
5!
700
                    data.pinnedResources.add(TemplateData.get().getTemplate(obj.stringValue()));
×
701
                } else if (st.getPredicate().equals(HAS_PINNED_QUERY) && st.getObject() instanceof IRI obj) {
5!
702
                    data.pinnedResources.add(GrlcQuery.get(obj.stringValue()));
×
703
                } else if (st.getPredicate().equals(NTEMPLATE.HAS_DEFAULT_PROVENANCE) && st.getObject() instanceof IRI obj) {
5!
704
                    data.defaultProvenance = obj;
1✔
705
                }
706
            } else if (st.getPredicate().equals(NTEMPLATE.HAS_TAG) && st.getObject() instanceof Literal l) {
×
707
                data.pinGroupTags.add(l.stringValue());
×
708
                Set<Serializable> list = data.pinnedResourceMap.get(l.stringValue());
×
709
                if (list == null) {
×
710
                    list = new HashSet<>();
×
711
                    data.pinnedResourceMap.put(l.stringValue(), list);
×
712
                }
713
                list.add(TemplateData.get().getTemplate(st.getSubject().stringValue()));
×
714
            }
715
        }
1✔
716
    }
1✔
717

718
    @Override
719
    public String toString() {
720
        return id;
×
721
    }
722

723
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc