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

knowledgepixels / nanodash / 19170532844

07 Nov 2025 01:56PM UTC coverage: 14.148% (-0.8%) from 14.95%
19170532844

push

github

tkuhn
feat(Space): Part-level elements for Spaces, also namespace-declared

518 of 4596 branches covered (11.27%)

Branch coverage included in aggregate %.

1335 of 8501 relevant lines covered (15.7%)

0.7 hits per line

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

41.26
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.HashMap;
6
import java.util.List;
7
import java.util.Map;
8
import java.util.Set;
9

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

18
public class MaintainedResource implements Serializable {
19

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

22
    private static List<MaintainedResource> resourceList;
23
    private static Map<String, MaintainedResource> resourcesById;
24
    private static Map<String, MaintainedResource> resourcesByNamespace;
25
    private static Map<Space, List<MaintainedResource>> resourcesBySpace;
26
    private static boolean loaded = false;
3✔
27

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

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

58
    /**
59
     * Ensure that the resources are loaded, fetching them from the API if necessary.
60
     */
61
    public static void ensureLoaded() {
62
        if (resourceList == null) {
2✔
63
            refresh(QueryApiAccess.forcedGet(new QueryRef("get-maintained-resources")));
6✔
64
        }
65
    }
1✔
66

67
    /**
68
     * Get the list of all maintained resources.
69
     *
70
     * @return List of resources.
71
     */
72
    public static List<MaintainedResource> getResourceList() {
73
        ensureLoaded();
×
74
        return resourceList;
×
75
    }
76

77
    public static List<MaintainedResource> getResourcesBySpace(Space space) {
78
        return resourcesBySpace.computeIfAbsent(space, k -> new ArrayList<>());
×
79
    }
80

81
    /**
82
     * Get a maintained resource by its id.
83
     *
84
     * @param id The id of the resource.
85
     * @return The corresponding MaintainedResource object, or null if not found.
86
     */
87
    public static MaintainedResource get(String id) {
88
        ensureLoaded();
1✔
89
        return resourcesById.get(id);
5✔
90
    }
91

92
    public static MaintainedResource getByNamespace(String namespace) {
93
        return resourcesByNamespace.get(namespace);
×
94
    }
95

96
    public static String getNamespace(Object stringOrIri) {
97
        return stringOrIri.toString().replaceFirst("([#/])[^#/]+$", "$1");
×
98
    }
99

100
    public static void refresh() {
101
        refresh(QueryApiAccess.forcedGet(new QueryRef("get-maintained-resources")));
6✔
102
        for (MaintainedResource resource : resourceList) {
10✔
103
            resource.dataNeedsUpdate = true;
3✔
104
        }
1✔
105
    }
1✔
106

107
    private String id, label, nanopubId, namespace;
108
    private Space space;
109
    private Nanopub nanopub;
110
    private ResourceData data = new ResourceData();
5✔
111

112
    private boolean dataInitialized = false;
3✔
113
    private boolean dataNeedsUpdate = true;
3✔
114

115
    private static class ResourceData implements Serializable {
2✔
116
        List<ViewDisplay> topLevelViews = new ArrayList<>();
5✔
117
        List<ViewDisplay> partLevelViews = new ArrayList<>();
6✔
118
    }
119

120
    private MaintainedResource(ApiResponseEntry resp, Space space) {
2✔
121
        this.space = space;
3✔
122
        this.id = resp.get("resource");
5✔
123
        this.label = resp.get("label");
5✔
124
        this.nanopubId = resp.get("np");
5✔
125
        this.namespace = resp.get("namespace");
5✔
126
        if (namespace != null && namespace.isBlank()) namespace = null;
10!
127
        this.nanopub = Utils.getAsNanopub(nanopubId);
5✔
128
    }
1✔
129

130
    public Space getSpace() {
131
        return space;
×
132
    }
133

134
    public String getId() {
135
        return id;
3✔
136
    }
137

138
    public String getNanopubId() {
139
        return nanopubId;
×
140
    }
141

142
    public Nanopub getNanopub() {
143
        return nanopub;
×
144
    }
145

146
    public String getLabel() {
147
        return label;
×
148
    }
149

150
    public String getNamespace() {
151
        return namespace;
3✔
152
    }
153

154
    public boolean isDataInitialized() {
155
        triggerDataUpdate();
×
156
        return dataInitialized;
×
157
    }
158

159
    public List<ViewDisplay> getTopLevelViews() {
160
        triggerDataUpdate();
×
161
        // TODO Check for compliance with target classes here too:
162
        return data.topLevelViews;
×
163
    }
164

165
    public List<ViewDisplay> getPartLevelViews(Set<IRI> classes) {
166
        triggerDataUpdate();
×
167
        List<ViewDisplay> viewDisplays = new ArrayList<>();
×
168
        for (ViewDisplay v : data.partLevelViews) {
×
169
            if (v.getView().hasTargetClasses()) {
×
170
                for (IRI c : classes) {
×
171
                    if (v.getView().hasTargetClass(c)) {
×
172
                        viewDisplays.add(v);
×
173
                        break;
×
174
                    }
175
                }
×
176
            } else {
177
                viewDisplays.add(v);
×
178
            }
179
        }
×
180
        return viewDisplays;
×
181
    }
182

183
    public boolean coversElement(String elementId) {
184
        triggerDataUpdate();
×
185
        for (ViewDisplay v : data.topLevelViews) {
×
186
            if (v.getView().coversElement(elementId)) return true;
×
187
        }
×
188
        for (ViewDisplay v : data.partLevelViews) {
×
189
            if (v.getView().coversElement(elementId)) return true;
×
190
        }
×
191
        return false;
×
192
    }
193

194
    private synchronized void triggerDataUpdate() {
195
        if (dataNeedsUpdate) {
×
196
            new Thread(() -> {
×
197
                try {
198
                    ResourceData newData = new ResourceData();
×
199

200
                    for (ApiResponseEntry r : QueryApiAccess.get(new QueryRef("get-view-displays", "resource", id)).getData()) {
×
201
                        if (!space.isAdminPubkey(r.get("pubkey"))) continue;
×
202
                        try {
203
                            ViewDisplay vd = new ViewDisplay(r);
×
204
                            if (ResourceView.PART_LEVEL_VIEW_DISPLAY.stringValue().equals(r.get("displayType"))) {
×
205
                                newData.partLevelViews.add(vd);
×
206
                            } else {
207
                                newData.topLevelViews.add(vd);
×
208
                            }
209
                        } catch (IllegalArgumentException ex) {
×
210
                            logger.error("Couldn't generate view display object", ex);
×
211
                        }
×
212
                    }
×
213
                    data = newData;
×
214
                    dataInitialized = true;
×
215
                } catch (Exception ex) {
×
216
                    logger.error("Error while trying to update space data: {}", ex);
×
217
                    dataNeedsUpdate = true;
×
218
                }
×
219
            }).start();
×
220
            dataNeedsUpdate = false;
×
221
        }
222
    }
×
223

224
    @Override
225
    public String toString() {
226
        return id;
×
227
    }
228

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