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

knowledgepixels / nanodash / 19767179367

28 Nov 2025 02:51PM UTC coverage: 14.034% (-0.4%) from 14.4%
19767179367

push

github

tkuhn
feat(MaintainedResource): Use new "appliesTo..." fields

526 of 4890 branches covered (10.76%)

Branch coverage included in aggregate %.

1414 of 8934 relevant lines covered (15.83%)

0.7 hits per line

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

40.52
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.HashSet;
8
import java.util.List;
9
import java.util.Map;
10
import java.util.Set;
11

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

20
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
21

22
public class MaintainedResource implements Serializable {
23

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

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

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

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

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

82
    public static void forceRootRefresh(long waitMillis) {
83
        resourceList = null;
×
84
        runRootUpdateAfter = System.currentTimeMillis() + waitMillis;
×
85
    }
×
86

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

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

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

112
    public static MaintainedResource getByNamespace(String namespace) {
113
        return resourcesByNamespace.get(namespace);
×
114
    }
115

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

120
    public static void refresh() {
121
        refresh(QueryApiAccess.forcedGet(new QueryRef("get-maintained-resources")));
6✔
122
        for (MaintainedResource resource : resourceList) {
10✔
123
            resource.dataNeedsUpdate = true;
3✔
124
        }
1✔
125
    }
1✔
126

127
    private String id, label, nanopubId, namespace;
128
    private Space space;
129
    private Nanopub nanopub;
130
    private ResourceData data = new ResourceData();
5✔
131

132
    private boolean dataInitialized = false;
3✔
133
    private boolean dataNeedsUpdate = true;
3✔
134

135
    private static class ResourceData implements Serializable {
2✔
136
        List<ViewDisplay> viewDisplays = new ArrayList<>();
6✔
137
    }
138

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

149
    public Space getSpace() {
150
        return space;
×
151
    }
152

153
    public String getId() {
154
        return id;
3✔
155
    }
156

157
    public String getNanopubId() {
158
        return nanopubId;
×
159
    }
160

161
    public Nanopub getNanopub() {
162
        return nanopub;
×
163
    }
164

165
    public String getLabel() {
166
        return label;
×
167
    }
168

169
    public String getNamespace() {
170
        return namespace;
3✔
171
    }
172

173
    public boolean isDataInitialized() {
174
        triggerDataUpdate();
×
175
        return dataInitialized;
×
176
    }
177

178
    public List<ViewDisplay> getViewDisplays(boolean toplevel, Set<IRI> classes) {
179
        triggerDataUpdate();
×
180
        List<ViewDisplay> viewDisplays = new ArrayList<>();
×
181
        Set<IRI> viewKinds = new HashSet<>();
×
182

183
        for (ViewDisplay vd : data.viewDisplays) {
×
184
            IRI kind = vd.getViewKindIri();
×
185
            if (kind != null) {
×
186
                if (viewKinds.contains(kind)) continue;
×
187
                viewKinds.add(vd.getViewKindIri());
×
188
            }
189
            if (vd.hasType(KPXL_TERMS.DEACTIVATED_VIEW_DISPLAY)) continue;
×
190

191
            if (vd.appliesTo(id, null)) {
×
192
                viewDisplays.add(vd);
×
193
            } else if (toplevel && vd.hasType(KPXL_TERMS.TOP_LEVEL_VIEW_DISPLAY)) {
×
194
                // Deprecated
195
                viewDisplays.add(vd);
×
196
            }
197
        }
×
198

199
        Collections.sort(viewDisplays);
×
200
        return viewDisplays;
×
201
    }
202

203
    public boolean appliesTo(String elementId, Set<IRI> classes) {
204
        triggerDataUpdate();
×
205
        for (ViewDisplay v : data.viewDisplays) {
×
206
            if (v.appliesTo(elementId, classes)) 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
                            newData.viewDisplays.add(ViewDisplay.get(r.get("display")));
×
221
                        } catch (IllegalArgumentException ex) {
×
222
                            logger.error("Couldn't generate view display object", ex);
×
223
                        }
×
224
                    }
×
225
                    data = newData;
×
226
                    dataInitialized = true;
×
227
                } catch (Exception ex) {
×
228
                    logger.error("Error while trying to update space data: {}", ex);
×
229
                    dataNeedsUpdate = true;
×
230
                }
×
231
            }).start();
×
232
            dataNeedsUpdate = false;
×
233
        }
234
    }
×
235

236
    @Override
237
    public String toString() {
238
        return id;
×
239
    }
240

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