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

knowledgepixels / nanodash / 19369164032

14 Nov 2025 03:23PM UTC coverage: 13.303% (-0.8%) from 14.107%
19369164032

push

github

tkuhn
feat(SpacePage): Show source links also for roles

502 of 4788 branches covered (10.48%)

Branch coverage included in aggregate %.

1309 of 8825 relevant lines covered (14.83%)

0.66 hits per line

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

35.63
src/main/java/com/knowledgepixels/nanodash/MaintainedResource.java
1
package com.knowledgepixels.nanodash;
2

3
import java.io.Serializable;
4
import java.util.ArrayList;
5
import java.util.Collections;
6
import java.util.HashMap;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.Set;
10

11
import org.eclipse.rdf4j.model.IRI;
12
import org.nanopub.Nanopub;
13
import org.nanopub.extra.services.ApiResponse;
14
import org.nanopub.extra.services.ApiResponseEntry;
15
import org.nanopub.extra.services.QueryRef;
16
import org.slf4j.Logger;
17
import org.slf4j.LoggerFactory;
18

19
public class MaintainedResource implements Serializable {
20

21
    private static final Logger logger = LoggerFactory.getLogger(MaintainedResource.class);
3✔
22

23
    private static List<MaintainedResource> resourceList;
24
    private static Map<String, MaintainedResource> resourcesById;
25
    private static Map<String, MaintainedResource> resourcesByNamespace;
26
    private static Map<Space, List<MaintainedResource>> resourcesBySpace;
27
    private static boolean loaded = false;
2✔
28
    private static Long runRootUpdateAfter = null;
3✔
29

30
    public static synchronized void refresh(ApiResponse resp) {
31
        resourceList = new ArrayList<>();
4✔
32
        resourcesById = new HashMap<>();
4✔
33
        resourcesBySpace = new HashMap<>();
4✔
34
        resourcesByNamespace = new HashMap<>();
4✔
35
        for (ApiResponseEntry entry : resp.getData()) {
11✔
36
            Space space = Space.get(entry.get("space"));
5✔
37
            if (space == null) continue;
3✔
38
            MaintainedResource resource = new MaintainedResource(entry, space);
6✔
39
            if (resourcesById.containsKey(resource.getId())) continue;
5!
40
            resourceList.add(resource);
4✔
41
            resourcesById.put(resource.getId(), resource);
6✔
42
            resourcesBySpace.computeIfAbsent(space, k -> new ArrayList<>()).add(resource);
12✔
43
            if (resource.getNamespace() != null) {
3✔
44
                // TODO Handle conflicts when two resources claim the same namespace:
45
                resourcesByNamespace.put(resource.getNamespace(), resource);
6✔
46
            }
47
        }
1✔
48
        loaded = true;
2✔
49
    }
1✔
50

51
    /**
52
     * Check if the resources have been loaded.
53
     *
54
     * @return true if loaded, false otherwise.
55
     */
56
    public static boolean isLoaded() {
57
        return loaded;
×
58
    }
59

60
    /**
61
     * Ensure that the resources are loaded, fetching them from the API if necessary.
62
     */
63
    public static void ensureLoaded() {
64
        if (resourceList == null) {
2✔
65
            try {
66
                if (runRootUpdateAfter != null) {
2!
67
                    while (System.currentTimeMillis() < runRootUpdateAfter) {
×
68
                        Thread.sleep(100);
×
69
                    }
70
                    runRootUpdateAfter = null;
×
71
                }
72
            } catch (InterruptedException ex) {
×
73
                logger.error("Interrupted", ex);
×
74
            }
1✔
75
            refresh(QueryApiAccess.forcedGet(new QueryRef("get-maintained-resources")));
6✔
76
        }
77
    }
1✔
78

79
    public static void forceRootRefresh(long waitMillis) {
80
        resourceList = null;
×
81
        runRootUpdateAfter = System.currentTimeMillis() + waitMillis;
×
82
    }
×
83

84
    /**
85
     * Get the list of all maintained resources.
86
     *
87
     * @return List of resources.
88
     */
89
    public static List<MaintainedResource> getResourceList() {
90
        ensureLoaded();
×
91
        return resourceList;
×
92
    }
93

94
    public static List<MaintainedResource> getResourcesBySpace(Space space) {
95
        return resourcesBySpace.computeIfAbsent(space, k -> new ArrayList<>());
×
96
    }
97

98
    /**
99
     * Get a maintained resource by its id.
100
     *
101
     * @param id The id of the resource.
102
     * @return The corresponding MaintainedResource object, or null if not found.
103
     */
104
    public static MaintainedResource get(String id) {
105
        ensureLoaded();
1✔
106
        return resourcesById.get(id);
5✔
107
    }
108

109
    public static MaintainedResource getByNamespace(String namespace) {
110
        return resourcesByNamespace.get(namespace);
×
111
    }
112

113
    public static String getNamespace(Object stringOrIri) {
114
        return stringOrIri.toString().replaceFirst("([#/])[^#/]+$", "$1");
×
115
    }
116

117
    public static void refresh() {
118
        refresh(QueryApiAccess.forcedGet(new QueryRef("get-maintained-resources")));
×
119
        for (MaintainedResource resource : resourceList) {
×
120
            resource.dataNeedsUpdate = true;
×
121
        }
×
122
    }
×
123

124
    private String id, label, nanopubId, namespace;
125
    private Space space;
126
    private Nanopub nanopub;
127
    private ResourceData data = new ResourceData();
5✔
128

129
    private boolean dataInitialized = false;
3✔
130
    private boolean dataNeedsUpdate = true;
3✔
131

132
    private static class ResourceData implements Serializable {
2✔
133
        List<ViewDisplay> topLevelViews = new ArrayList<>();
5✔
134
        List<ViewDisplay> partLevelViews = new ArrayList<>();
6✔
135
    }
136

137
    private MaintainedResource(ApiResponseEntry resp, Space space) {
2✔
138
        this.space = space;
3✔
139
        this.id = resp.get("resource");
5✔
140
        this.label = resp.get("label");
5✔
141
        this.nanopubId = resp.get("np");
5✔
142
        this.namespace = resp.get("namespace");
5✔
143
        if (namespace != null && namespace.isBlank()) namespace = null;
10!
144
        this.nanopub = Utils.getAsNanopub(nanopubId);
5✔
145
    }
1✔
146

147
    public Space getSpace() {
148
        return space;
×
149
    }
150

151
    public String getId() {
152
        return id;
3✔
153
    }
154

155
    public String getNanopubId() {
156
        return nanopubId;
×
157
    }
158

159
    public Nanopub getNanopub() {
160
        return nanopub;
×
161
    }
162

163
    public String getLabel() {
164
        return label;
×
165
    }
166

167
    public String getNamespace() {
168
        return namespace;
3✔
169
    }
170

171
    public boolean isDataInitialized() {
172
        triggerDataUpdate();
×
173
        return dataInitialized;
×
174
    }
175

176
    public List<ViewDisplay> getTopLevelViews() {
177
        triggerDataUpdate();
×
178
        // TODO Check for compliance with target classes here too:
179
        return data.topLevelViews;
×
180
    }
181

182
    public List<ViewDisplay> getPartLevelViews(Set<IRI> classes) {
183
        triggerDataUpdate();
×
184
        List<ViewDisplay> viewDisplays = new ArrayList<>();
×
185
        for (ViewDisplay v : data.partLevelViews) {
×
186
            if (v.getView().hasTargetClasses()) {
×
187
                for (IRI c : classes) {
×
188
                    if (v.getView().hasTargetClass(c)) {
×
189
                        viewDisplays.add(v);
×
190
                        break;
×
191
                    }
192
                }
×
193
            } else {
194
                viewDisplays.add(v);
×
195
            }
196
        }
×
197
        return viewDisplays;
×
198
    }
199

200
    public boolean coversElement(String elementId) {
201
        triggerDataUpdate();
×
202
        for (ViewDisplay v : data.topLevelViews) {
×
203
            if (v.getView().coversElement(elementId)) return true;
×
204
        }
×
205
        for (ViewDisplay v : data.partLevelViews) {
×
206
            if (v.getView().coversElement(elementId)) return true;
×
207
        }
×
208
        return false;
×
209
    }
210

211
    private synchronized void triggerDataUpdate() {
212
        if (dataNeedsUpdate) {
×
213
            new Thread(() -> {
×
214
                try {
215
                    ResourceData newData = new ResourceData();
×
216

217
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-view-displays", "resource", id)).getData()) {
×
218
                        if (!space.isAdminPubkey(r.get("pubkey"))) continue;
×
219
                        try {
220
                            ViewDisplay vd = ViewDisplay.get(r.get("display"));
×
221
                            if (ResourceView.PART_LEVEL_VIEW_DISPLAY.stringValue().equals(r.get("displayType"))) {
×
222
                                newData.partLevelViews.add(vd);
×
223
                            } else {
224
                                newData.topLevelViews.add(vd);
×
225
                            }
226
                        } catch (IllegalArgumentException ex) {
×
227
                            logger.error("Couldn't generate view display object", ex);
×
228
                        }
×
229
                    }
×
230
                    Collections.sort(newData.topLevelViews);
×
231
                    Collections.sort(newData.partLevelViews);
×
232
                    data = newData;
×
233
                    dataInitialized = true;
×
234
                } catch (Exception ex) {
×
235
                    logger.error("Error while trying to update space data: {}", ex);
×
236
                    dataNeedsUpdate = true;
×
237
                }
×
238
            }).start();
×
239
            dataNeedsUpdate = false;
×
240
        }
241
    }
×
242

243
    @Override
244
    public String toString() {
245
        return id;
×
246
    }
247

248
}
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