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

knowledgepixels / nanodash / 28936601485

08 Jul 2026 10:43AM UTC coverage: 28.314% (+0.2%) from 28.115%
28936601485

Pull #545

github

web-flow
Merge 2842605b1 into 3cb376d20
Pull Request #545: feat: identify templates by embedded IRIs with dual-mode parser

1838 of 7333 branches covered (25.06%)

Branch coverage included in aggregate %.

3749 of 12399 relevant lines covered (30.24%)

4.5 hits per line

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

38.71
src/main/java/com/knowledgepixels/nanodash/template/TemplateData.java
1
package com.knowledgepixels.nanodash.template;
2

3
import com.knowledgepixels.nanodash.ApiCache;
4
import com.knowledgepixels.nanodash.QueryApiAccess;
5
import com.knowledgepixels.nanodash.Utils;
6
import net.trustyuri.TrustyUriUtils;
7
import org.eclipse.rdf4j.model.IRI;
8
import org.eclipse.rdf4j.model.Statement;
9
import org.nanopub.Nanopub;
10
import org.nanopub.extra.services.ApiResponse;
11
import org.nanopub.extra.services.ApiResponseEntry;
12
import org.nanopub.extra.services.QueryRef;
13
import org.nanopub.vocabulary.NTEMPLATE;
14
import org.slf4j.Logger;
15
import org.slf4j.LoggerFactory;
16

17
import java.io.Serializable;
18
import java.util.*;
19
import java.util.concurrent.ConcurrentHashMap;
20
import java.util.concurrent.ConcurrentMap;
21

22
/**
23
 * Singleton class that manages templates data.
24
 */
25
public class TemplateData implements Serializable {
26

27
    private static final Logger logger = LoggerFactory.getLogger(TemplateData.class);
9✔
28

29
    private static TemplateData instance;
30

31
    /**
32
     * Refreshes the templates data by creating a new instance of TemplateData.
33
     */
34
    public static synchronized void refreshTemplates() {
35
        logger.info("Refreshing templates...");
9✔
36
        instance = new TemplateData();
12✔
37
    }
3✔
38

39
    /**
40
     * Ensures that the TemplateData instance is loaded.
41
     */
42
    public static synchronized void ensureLoaded() {
43
        if (instance == null) refreshTemplates();
6!
44
    }
3✔
45

46
    /**
47
     * Gets the singleton instance of TemplateData.
48
     *
49
     * @return the TemplateData instance
50
     */
51
    public static TemplateData get() {
52
        ensureLoaded();
3✔
53
        return instance;
6✔
54
    }
55

56
    private List<ApiResponseEntry> assertionTemplates, provenanceTemplates, pubInfoTemplates;
57
    private ConcurrentMap<String, Template> templateMap;
58

59
    /**
60
     * Constructor to initialize the TemplateData instance.
61
     */
62
    public TemplateData() {
6✔
63
        assertionTemplates = new ArrayList<>();
15✔
64
        provenanceTemplates = new ArrayList<>();
15✔
65
        pubInfoTemplates = new ArrayList<>();
15✔
66
        templateMap = new ConcurrentHashMap<>();
15✔
67
        refreshTemplates(assertionTemplates, QueryApiAccess.GET_ASSERTION_TEMPLATES);
15✔
68
        refreshTemplates(provenanceTemplates, QueryApiAccess.GET_PROVENANCE_TEMPLATES);
15✔
69
        refreshTemplates(pubInfoTemplates, QueryApiAccess.GET_PUBINFO_TEMPLATES);
15✔
70
    }
3✔
71

72
    private void refreshTemplates(List<ApiResponseEntry> templates, String queryId) {
73
        ApiResponse templateEntries = ApiCache.retrieveResponseSync(new QueryRef(queryId), true);
21✔
74
        String previousId = null;
6✔
75
        logger.info("Loading templates...");
9✔
76
        for (ApiResponseEntry entry : templateEntries.getData()) {
33✔
77
            if ("true".equals(entry.get("unlisted"))) continue;
21✔
78
            if (!entry.get("np").equals(previousId)) {
18✔
79
                templates.add(entry);
12✔
80
            }
81
            previousId = entry.get("np");
12✔
82
        }
3✔
83
        templates.sort(templateComparator);
9✔
84
    }
3✔
85

86
    /**
87
     * Returns the list of assertion templates.
88
     *
89
     * @return a list of assertion templates
90
     */
91
    public List<ApiResponseEntry> getAssertionTemplates() {
92
        return assertionTemplates;
×
93
    }
94

95
    /**
96
     * Returns the list of provenance templates.
97
     *
98
     * @return a list of provenance templates
99
     */
100
    public List<ApiResponseEntry> getProvenanceTemplates() {
101
        return provenanceTemplates;
×
102
    }
103

104
    /**
105
     * Returns the list of publication information templates.
106
     *
107
     * @return a list of publication information templates
108
     */
109
    public List<ApiResponseEntry> getPubInfoTemplates() {
110
        return pubInfoTemplates;
×
111
    }
112

113
    /**
114
     * Returns a Template object for the given template ID. The ID may be either
115
     * form: the nanopublication URI, or (for templates with embedded identity) the
116
     * embedded template-node IRI.
117
     *
118
     * @param id the ID of the template
119
     * @return the Template object if found, or null if not found or invalid
120
     */
121
    public Template getTemplate(String id) {
122
        Template template = templateMap.get(id);
18✔
123
        if (template != null) return template;
12✔
124
        String npId = Utils.stripToNanopubId(id);
9✔
125
        template = templateMap.get(npId);
18✔
126
        if (template != null) {
6!
127
            templateMap.put(id, template);
×
128
            return template;
×
129
        }
130
        if (TrustyUriUtils.isPotentialTrustyUri(npId)) {
9!
131
            try {
132
                Template t = new Template(npId);
15✔
133
                templateMap.put(id, t);
18✔
134
                templateMap.put(npId, t);
18✔
135
                templateMap.put(t.getId(), t);
21✔
136
                return t;
6✔
137
            } catch (Exception ex) {
×
138
                logger.error("Exception: {}", ex.getMessage());
×
139
                return null;
×
140
            }
141
        }
142
        return null;
×
143
    }
144

145
    /**
146
     * Registers a template from a Nanopub object directly, without fetching it from the registry.
147
     * This is useful for previewing templates that have not yet been published.
148
     *
149
     * @param np the Nanopub containing the template definition
150
     * @return the Template object, or null if the nanopub is not a valid template
151
     */
152
    public Template registerTemplate(Nanopub np) {
153
        String id = np.getUri().stringValue();
12✔
154
        Template template = templateMap.get(id);
18✔
155
        if (template != null) return template;
6!
156
        try {
157
            Template t = new Template(np);
15✔
158
            templateMap.put(id, t);
18✔
159
            templateMap.put(t.getId(), t);
21✔
160
            return t;
6✔
161
        } catch (Exception ex) {
×
162
            logger.error("Exception registering template from nanopub: {}", ex.getMessage());
×
163
            return null;
×
164
        }
165
    }
166

167
    /**
168
     * Resolves a template ID to the ID of the latest version of that template,
169
     * following the supersedes chain of the containing nanopublication. Accepts
170
     * either ID form (nanopublication URI or embedded template-node IRI) and
171
     * returns the latest version's canonical ID ({@link Template#getId()}) — so
172
     * even when there is no newer version, the given ID is normalized to canonical
173
     * form. Falls back to the given ID if the template cannot be loaded.
174
     *
175
     * @param templateId the ID of the template
176
     * @return the canonical ID of the latest version, or the given ID as fallback
177
     */
178
    public String getLatestTemplateId(String templateId) {
179
        String npId = Utils.stripToNanopubId(templateId);
×
180
        String latestNpId = QueryApiAccess.getLatestVersionId(npId);
×
181
        Template latest = getTemplate(latestNpId);
×
182
        if (latest != null) return latest.getId();
×
183
        return templateId;
×
184
    }
185

186
    /**
187
     * Returns a Template object for the template of the given Nanopub.
188
     *
189
     * @param np the Nanopub from which to extract the template
190
     * @return the Template object if found, or null if not found or invalid
191
     */
192
    public Template getTemplate(Nanopub np) {
193
        IRI templateId = getTemplateId(np);
×
194
        if (templateId == null) return null;
×
195
        return getTemplate(templateId.stringValue());
×
196
    }
197

198
    /**
199
     * Returns a Template object for the provenance template of the given Nanopub.
200
     *
201
     * @param np the Nanopub from which to extract the provenance template
202
     * @return the Template object if found, or null if not found or invalid
203
     */
204
    public Template getProvenanceTemplate(Nanopub np) {
205
        IRI templateId = getProvenanceTemplateId(np);
×
206
        if (templateId == null) return null;
×
207
        return getTemplate(templateId.stringValue());
×
208
    }
209

210
    /**
211
     * Returns a set of Template objects for the publication information templates of the given Nanopub.
212
     *
213
     * @param np the Nanopub from which to extract the publication information templates
214
     * @return a set of Template objects
215
     */
216
    public Set<Template> getPubinfoTemplates(Nanopub np) {
217
        Set<Template> templates = new HashSet<>();
×
218
        for (IRI id : getPubinfoTemplateIds(np)) {
×
219
            templates.add(getTemplate(id.stringValue()));
×
220
        }
×
221
        return templates;
×
222
    }
223

224
    /**
225
     * Returns the template ID of the given Nanopub.
226
     *
227
     * @param nanopub the Nanopub from which to extract the template ID
228
     * @return the IRI of the template ID, or null if not found
229
     */
230
    public IRI getTemplateId(Nanopub nanopub) {
231
        for (Statement st : nanopub.getPubinfo()) {
×
232
            if (!st.getSubject().equals(nanopub.getUri())) continue;
×
233
            if (!st.getPredicate().equals(NTEMPLATE.WAS_CREATED_FROM_TEMPLATE)) continue;
×
234
            if (!(st.getObject() instanceof IRI)) continue;
×
235
            return (IRI) st.getObject();
×
236
        }
237
        return null;
×
238
    }
239

240
    /**
241
     * Returns the provenance template ID of the given Nanopub.
242
     *
243
     * @param nanopub the Nanopub from which to extract the provenance template ID
244
     * @return the IRI of the provenance template ID, or null if not found
245
     */
246
    public IRI getProvenanceTemplateId(Nanopub nanopub) {
247
        for (Statement st : nanopub.getPubinfo()) {
×
248
            if (!st.getSubject().equals(nanopub.getUri())) continue;
×
249
            if (!st.getPredicate().equals(NTEMPLATE.WAS_CREATED_FROM_PROVENANCE_TEMPLATE)) continue;
×
250
            if (!(st.getObject() instanceof IRI)) continue;
×
251
            return (IRI) st.getObject();
×
252
        }
253
        return null;
×
254
    }
255

256
    /**
257
     * Returns the set of publication information template IDs for the given Nanopub.
258
     *
259
     * @param nanopub the Nanopub from which to extract the publication information template IDs
260
     * @return a set of IRI objects representing the publication information template IDs
261
     */
262
    public Set<IRI> getPubinfoTemplateIds(Nanopub nanopub) {
263
        Set<IRI> iriSet = new HashSet<>();
×
264
        for (Statement st : nanopub.getPubinfo()) {
×
265
            if (!st.getSubject().equals(nanopub.getUri())) continue;
×
266
            if (!st.getPredicate().equals(NTEMPLATE.WAS_CREATED_FROM_PUBINFO_TEMPLATE)) continue;
×
267
            if (!(st.getObject() instanceof IRI)) continue;
×
268
            iriSet.add((IRI) st.getObject());
×
269
        }
×
270
        return iriSet;
×
271
    }
272

273
    /**
274
     * Returns a list of Template objects from the given ApiResponse.
275
     *
276
     * @param apiResponse the ApiResponse containing template entries
277
     * @return a list of Template objects
278
     */
279
    public static List<Template> getTemplateList(ApiResponse apiResponse) {
280
        List<Template> templates = new ArrayList<>();
×
281
        for (ApiResponseEntry e : apiResponse.getData()) {
×
282
            String templateNpId = e.get("template_np");
×
283
            if (templateNpId == null) templateNpId = e.get("np");
×
284
            templates.add(TemplateData.get().getTemplate(templateNpId));
×
285
        }
×
286
        return templates;
×
287
    }
288

289
    private static final TemplateComparator templateComparator = new TemplateComparator();
15✔
290

291
    private static class TemplateComparator implements Comparator<ApiResponseEntry>, Serializable {
292

293
        /**
294
         * Compares two Template objects based on their labels.
295
         *
296
         * @param o1 the first object to be compared.
297
         * @param o2 the second object to be compared.
298
         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
299
         */
300
        @Override
301
        public int compare(ApiResponseEntry o1, ApiResponseEntry o2) {
302
            return o1.get("label").compareTo(o2.get("label"));
24✔
303
        }
304

305
    }
306

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