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

knowledgepixels / nanodash / 24880477649

24 Apr 2026 08:39AM UTC coverage: 18.287% (+0.8%) from 17.496%
24880477649

push

github

web-flow
Merge pull request #453 from knowledgepixels/add-root-nanopub-placeholder

feat: support RootNanopubPlaceholder in templates

956 of 6218 branches covered (15.37%)

Branch coverage included in aggregate %.

2251 of 11319 relevant lines covered (19.89%)

2.76 hits per line

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

57.54
src/main/java/com/knowledgepixels/nanodash/template/Template.java
1
package com.knowledgepixels.nanodash.template;
2

3
import com.knowledgepixels.nanodash.LookupApis;
4
import com.knowledgepixels.nanodash.Utils;
5
import net.trustyuri.TrustyUriUtils;
6
import org.eclipse.rdf4j.common.exception.RDF4JException;
7
import org.eclipse.rdf4j.model.*;
8
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
9
import org.eclipse.rdf4j.model.util.Literals;
10
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
11
import org.eclipse.rdf4j.model.vocabulary.RDF;
12
import org.eclipse.rdf4j.model.vocabulary.RDFS;
13
import org.eclipse.rdf4j.model.vocabulary.SHACL;
14
import org.nanopub.Nanopub;
15
import org.nanopub.NanopubUtils;
16
import org.nanopub.vocabulary.NTEMPLATE;
17
import org.slf4j.Logger;
18
import org.slf4j.LoggerFactory;
19

20
import java.io.Serializable;
21
import java.util.*;
22

23
/**
24
 * Represents a template for creating nanopublications.
25
 */
26
public class Template implements Serializable {
27

28
    private static final ValueFactory vf = SimpleValueFactory.getInstance();
6✔
29
    private static final Logger logger = LoggerFactory.getLogger(Template.class);
9✔
30

31
    /**
32
     * Default target namespace for templates.
33
     */
34
    public static final String DEFAULT_TARGET_NAMESPACE = "https://w3id.org/np/";
35

36
    // TODO Move this to the other ntemplate vocabulary terms in nanopub-java:
37
    private static final IRI ADVANCED_STATEMENT = vf.createIRI("https://w3id.org/np/o/ntemplate/AdvancedStatement");
15✔
38

39
    private final Nanopub nanopub;
40
    private String label;
41
    private String description;
42

43
    // TODO: Make all these maps more generic and the code simpler:
44
    private IRI templateIri;
45
    private Map<IRI, List<IRI>> typeMap = new HashMap<>();
15✔
46
    private Map<IRI, List<Value>> possibleValueMap = new HashMap<>();
15✔
47
    private Map<IRI, List<IRI>> possibleValuesToLoadMap = new HashMap<>();
15✔
48
    private Map<IRI, List<String>> apiMap = new HashMap<>();
15✔
49
    private Map<IRI, String> labelMap = new HashMap<>();
15✔
50
    private Map<IRI, IRI> datatypeMap = new HashMap<>();
15✔
51
    private Map<IRI, String> languageTagMap = new HashMap<>();
15✔
52
    private Map<IRI, String> prefixMap = new HashMap<>();
15✔
53
    private Map<IRI, String> prefixLabelMap = new HashMap<>();
15✔
54
    private Map<IRI, String> regexMap = new HashMap<>();
15✔
55
    private Map<IRI, List<IRI>> statementMap = new HashMap<>();
15✔
56
    private Map<IRI, IRI> statementSubjects = new HashMap<>();
15✔
57
    private Map<IRI, IRI> statementPredicates = new HashMap<>();
15✔
58
    private Map<IRI, Value> statementObjects = new HashMap<>();
15✔
59
    private Map<IRI, Integer> statementOrder = new HashMap<>();
15✔
60
    private IRI defaultProvenance;
61
    private List<IRI> requiredPubInfoElements = new ArrayList<>();
15✔
62
    private String tag = null;
9✔
63
    private Map<IRI, Value> defaultValues = new HashMap<>();
15✔
64
    private String targetNamespace = null;
9✔
65
    private String nanopubLabelPattern;
66
    private List<IRI> targetNanopubTypes = new ArrayList<>();
15✔
67

68
    /**
69
     * Creates a Template object from a template id.
70
     *
71
     * @param templateId the id of the template, which is the URI of a nanopublication that contains the template definition.
72
     * @throws RDF4JException             if there is an error retrieving the nanopublication.
73
     * @throws MalformedTemplateException if the template is malformed or not a valid nanopub template.
74
     */
75
    Template(String templateId) throws RDF4JException, MalformedTemplateException {
76
        this(Utils.getNanopub(templateId));
12✔
77
    }
3✔
78

79
    /**
80
     * Creates a Template object from a Nanopub object.
81
     *
82
     * @param np the Nanopub object containing the template definition.
83
     * @throws MalformedTemplateException if the template is malformed or not a valid nanopub template.
84
     */
85
    Template(Nanopub np) throws MalformedTemplateException {
6✔
86
        nanopub = np;
9✔
87
        processTemplate(nanopub);
12✔
88
    }
3✔
89

90
    /**
91
     * Checks if the template is unlisted.
92
     *
93
     * @return true if the template is unlisted, false otherwise.
94
     */
95
    public boolean isUnlisted() {
96
        return typeMap.get(templateIri).contains(NTEMPLATE.UNLISTED_TEMPLATE);
×
97
    }
98

99
    /**
100
     * Returns the Nanopub object representing the template.
101
     *
102
     * @return the Nanopub object of the template.
103
     */
104
    public Nanopub getNanopub() {
105
        return nanopub;
×
106
    }
107

108
    /**
109
     * Returns the ID of the template, which is the URI of the nanopublication.
110
     *
111
     * @return the ID of the template as a string.
112
     */
113
    public String getId() {
114
        return nanopub.getUri().toString();
×
115
    }
116

117
    /**
118
     * Returns the label of the template.
119
     *
120
     * @return the label of the template, or a default label if not set.
121
     */
122
    public String getLabel() {
123
        if (label == null) {
9!
124
            return "Template " + TrustyUriUtils.getArtifactCode(nanopub.getUri().stringValue()).substring(0, 10);
×
125
        }
126
        return label;
9✔
127
    }
128

129
    /**
130
     * Returns the description of the template.
131
     *
132
     * @return the description of the template, or an empty string if not set.
133
     */
134
    public String getDescription() {
135
        return description;
9✔
136
    }
137

138
    /**
139
     * Returns the IRI of the template.
140
     *
141
     * @param iri the IRI to transform.
142
     * @return the transformed IRI, or the original IRI if no transformation is needed.
143
     */
144
    public String getLabel(IRI iri) {
145
        iri = transform(iri);
×
146
        return labelMap.get(iri);
×
147
    }
148

149
    /**
150
     * Returns the IRI of the template, transforming it if necessary.
151
     *
152
     * @param iri the IRI to transform.
153
     * @return the transformed IRI, or the original IRI if no transformation is needed.
154
     */
155
    public IRI getFirstOccurrence(IRI iri) {
156
        for (IRI i : getStatementIris()) {
×
157
            if (statementMap.containsKey(i)) {
×
158
                // grouped statement
159
                for (IRI g : getStatementIris(i)) {
×
160
                    if (iri.equals(statementSubjects.get(g))) return g;
×
161
                    if (iri.equals(statementPredicates.get(g))) return g;
×
162
                    if (iri.equals(statementObjects.get(g))) return g;
×
163
                }
×
164
            } else {
165
                // non-grouped statement
166
                if (iri.equals(statementSubjects.get(i))) return i;
×
167
                if (iri.equals(statementPredicates.get(i))) return i;
×
168
                if (iri.equals(statementObjects.get(i))) return i;
×
169
            }
170
        }
×
171
        return null;
×
172
    }
173

174
    /**
175
     * Returns the datatype for the given literal placeholder IRI.
176
     *
177
     * @param iri the literal placeholder IRI.
178
     * @return the datatype for the literal.
179
     */
180
    public IRI getDatatype(IRI iri) {
181
        iri = transform(iri);
×
182
        return datatypeMap.get(iri);
×
183
    }
184

185
    /**
186
     * Returns the language tag for the given literal placeholder IRI.
187
     *
188
     * @param iri the literal placeholder IRI.
189
     * @return the language tag for the literal.
190
     */
191
    public String getLanguageTag(IRI iri) {
192
        iri = transform(iri);
×
193
        return languageTagMap.get(iri);
×
194
    }
195

196
    /**
197
     * Returns the prefix for the given IRI.
198
     *
199
     * @param iri the IRI.
200
     * @return the prefix for the IRI.
201
     */
202
    public String getPrefix(IRI iri) {
203
        iri = transform(iri);
×
204
        return prefixMap.get(iri);
×
205
    }
206

207
    /**
208
     * Returns the prefix label for a given IRI.
209
     *
210
     * @param iri the IRI to get the prefix label for.
211
     * @return the prefix label for the IRI, or null if not found.
212
     */
213
    public String getPrefixLabel(IRI iri) {
214
        iri = transform(iri);
×
215
        return prefixLabelMap.get(iri);
×
216
    }
217

218
    /**
219
     * Returns the regex pattern for a given IRI.
220
     *
221
     * @param iri the IRI to get the regex for.
222
     * @return the regex pattern for the IRI, or null if not found.
223
     */
224
    public String getRegex(IRI iri) {
225
        iri = transform(iri);
×
226
        return regexMap.get(iri);
×
227
    }
228

229
    /**
230
     * Transforms an IRI by removing the artifact code if it is present.
231
     *
232
     * @param iri the IRI to transform.
233
     * @return the transformed IRI, or the original IRI if no transformation is needed.
234
     */
235
    public Value getDefault(IRI iri) {
236
        if (iri.stringValue().matches(".*__[0-9]+")) {
×
237
            String baseIri = iri.stringValue().replaceFirst("__[0-9]+$", "");
×
238
            Value v = defaultValues.get(vf.createIRI(baseIri));
×
239
            if (v instanceof IRI vIri) {
×
240
                // Append repeat suffix only for local URIs; fixed URIs stay unchanged
241
                if (vIri.stringValue().startsWith(nanopub.getUri().stringValue())) {
×
242
                    int repeatSuffix = Integer.parseInt(iri.stringValue().replaceFirst("^.*__([0-9]+)$", "$1"));
×
243
                    return vf.createIRI(vIri.stringValue() + (repeatSuffix + 1));
×
244
                }
245
                return vIri;
×
246
            }
247
        }
248
        iri = transform(iri);
×
249
        return defaultValues.get(iri);
×
250
    }
251

252
    /**
253
     * Returns the statement IRIs associated with the template.
254
     *
255
     * @return the list of statement IRIs for the template.
256
     */
257
    public List<IRI> getStatementIris() {
258
        return statementMap.get(templateIri);
×
259
    }
260

261
    /**
262
     * Returns the statement IRIs associated with a specific group IRI.
263
     *
264
     * @param groupIri the IRI of the group for which to retrieve statement IRIs.
265
     * @return the list of statement IRIs for the specified group IRI, or null if no statements are associated with that group.
266
     */
267
    public List<IRI> getStatementIris(IRI groupIri) {
268
        return statementMap.get(groupIri);
18✔
269
    }
270

271
    /**
272
     * Returns the subject, predicate, and object of a statement given its IRI.
273
     *
274
     * @param statementIri the IRI of the statement to retrieve.
275
     * @return the subject, predicate, and object of the statement as a triple.
276
     */
277
    public IRI getSubject(IRI statementIri) {
278
        return statementSubjects.get(statementIri);
×
279
    }
280

281
    /**
282
     * Returns the predicate of a statement given its IRI.
283
     *
284
     * @param statementIri the IRI of the statement to retrieve.
285
     * @return the predicate of the statement, or null if not found.
286
     */
287
    public IRI getPredicate(IRI statementIri) {
288
        return statementPredicates.get(statementIri);
×
289
    }
290

291
    /**
292
     * Returns the object of a statement given its IRI.
293
     *
294
     * @param statementIri the IRI of the statement to retrieve.
295
     * @return the object of the statement, or null if not found.
296
     */
297
    public Value getObject(IRI statementIri) {
298
        return statementObjects.get(statementIri);
×
299
    }
300

301
    /**
302
     * Checks if the template is a local resource.
303
     *
304
     * @param iri the IRI to check.
305
     * @return true if the IRI is a local resource, false otherwise.
306
     */
307
    public boolean isLocalResource(IRI iri) {
308
        iri = transform(iri);
12✔
309
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.LOCAL_RESOURCE);
21!
310
    }
311

312
    /**
313
     * Checks if the template is an introduced resource.
314
     *
315
     * @param iri the IRI to check.
316
     * @return true if the IRI is an introduced resource, false otherwise.
317
     */
318
    public boolean isIntroducedResource(IRI iri) {
319
        iri = transform(iri);
12✔
320
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.INTRODUCED_RESOURCE);
21!
321
    }
322

323
    /**
324
     * Checks if the template is an embedded resource.
325
     *
326
     * @param iri the IRI to check.
327
     * @return true if the IRI is an embedded resource, false otherwise.
328
     */
329
    public boolean isEmbeddedResource(IRI iri) {
330
        iri = transform(iri);
12✔
331
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.EMBEDDED_RESOURCE);
21!
332
    }
333

334
    /**
335
     * Checks if the IRI is a value placeholder.
336
     *
337
     * @param iri the IRI to check.
338
     * @return true if the IRI is a value placeholder, false otherwise.
339
     */
340
    public boolean isValuePlaceholder(IRI iri) {
341
        iri = transform(iri);
12✔
342
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.VALUE_PLACEHOLDER);
21!
343
    }
344

345
    /**
346
     * Checks if the IRI is a URI placeholder.
347
     *
348
     * @param iri the IRI to check.
349
     * @return true if the IRI is a URI placeholder, false otherwise.
350
     */
351
    public boolean isUriPlaceholder(IRI iri) {
352
        iri = transform(iri);
12✔
353
        if (!typeMap.containsKey(iri)) return false;
21!
354
        for (IRI t : typeMap.get(iri)) {
×
355
            if (t.equals(NTEMPLATE.URI_PLACEHOLDER)) return true;
×
356
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
×
357
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
×
358
            if (t.equals(NTEMPLATE.AUTO_ESCAPE_URI_PLACEHOLDER)) return true;
×
359
            if (t.equals(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER)) return true;
×
360
            if (t.equals(NTEMPLATE.GUIDED_CHOICE_PLACEHOLDER)) return true;
×
361
            if (t.equals(NTEMPLATE.AGENT_PLACEHOLDER)) return true;
×
362
        }
×
363
        return false;
×
364
    }
365

366
    /**
367
     * Checks if the IRI is an external URI placeholder.
368
     *
369
     * @param iri the IRI to check.
370
     * @return true if the IRI is an external URI placeholder, false otherwise.
371
     */
372
    public boolean isExternalUriPlaceholder(IRI iri) {
373
        iri = transform(iri);
×
374
        if (!typeMap.containsKey(iri)) return false;
×
375
        for (IRI t : typeMap.get(iri)) {
×
376
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
×
377
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
×
378
        }
×
379
        return false;
×
380
    }
381

382
    /**
383
     * Checks if the IRI is a trusty URI placeholder.
384
     *
385
     * @param iri the IRI to check.
386
     * @return true if the IRI is a trusty URI placeholder, false otherwise.
387
     */
388
    public boolean isTrustyUriPlaceholder(IRI iri) {
389
        iri = transform(iri);
×
390
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.TRUSTY_URI_PLACEHOLDER);
×
391
    }
392

393
    /**
394
     * Checks if the IRI is an auto-escape URI placeholder.
395
     *
396
     * @param iri the IRI to check.
397
     * @return true if the IRI is an auto-escape URI placeholder, false otherwise.
398
     */
399
    public boolean isAutoEscapePlaceholder(IRI iri) {
400
        iri = transform(iri);
×
401
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.AUTO_ESCAPE_URI_PLACEHOLDER);
×
402
    }
403

404
    /**
405
     * Checks if the IRI is a literal placeholder.
406
     *
407
     * @param iri the IRI to check.
408
     * @return true if the IRI is a literal placeholder, false otherwise.
409
     */
410
    public boolean isLiteralPlaceholder(IRI iri) {
411
        iri = transform(iri);
12✔
412
        return typeMap.containsKey(iri) && (typeMap.get(iri).contains(NTEMPLATE.LITERAL_PLACEHOLDER) || typeMap.get(iri).contains(NTEMPLATE.LONG_LITERAL_PLACEHOLDER));
21!
413
    }
414

415
    /**
416
     * Checks if the IRI is a long literal placeholder.
417
     *
418
     * @param iri the IRI to check.
419
     * @return true if the IRI is a long literal placeholder, false otherwise.
420
     */
421
    public boolean isLongLiteralPlaceholder(IRI iri) {
422
        iri = transform(iri);
×
423
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.LONG_LITERAL_PLACEHOLDER);
×
424
    }
425

426
    /**
427
     * Checks if the IRI is a restricted choice placeholder.
428
     *
429
     * @param iri the IRI to check.
430
     * @return true if the IRI is a restricted choice placeholder, false otherwise.
431
     */
432
    public boolean isRestrictedChoicePlaceholder(IRI iri) {
433
        iri = transform(iri);
12✔
434
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER);
21!
435
    }
436

437
    /**
438
     * Checks if the IRI is a guided choice placeholder.
439
     *
440
     * @param iri the IRI to check.
441
     * @return true if the IRI is a guided choice placeholder, false otherwise.
442
     */
443
    public boolean isGuidedChoicePlaceholder(IRI iri) {
444
        iri = transform(iri);
×
445
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.GUIDED_CHOICE_PLACEHOLDER);
×
446
    }
447

448
    /**
449
     * Checks if the IRI is an agent placeholder.
450
     *
451
     * @param iri the IRI to check.
452
     * @return true if the IRI is an agent placeholder, false otherwise.
453
     */
454
    public boolean isAgentPlaceholder(IRI iri) {
455
        iri = transform(iri);
×
456
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.AGENT_PLACEHOLDER);
×
457
    }
458

459
    /**
460
     * Checks if the IRI is a sequence element placeholder.
461
     *
462
     * @param iri the IRI to check.
463
     * @return true if the IRI is a sequence element placeholder, false otherwise.
464
     */
465
    public boolean isSequenceElementPlaceholder(IRI iri) {
466
        iri = transform(iri);
12✔
467
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.SEQUENCE_ELEMENT_PLACEHOLDER);
21!
468
    }
469

470
    /**
471
     * Checks if the IRI is a root nanopub placeholder.
472
     *
473
     * @param iri the IRI to check.
474
     * @return true if the IRI is a root nanopub placeholder, false otherwise.
475
     */
476
    public boolean isRootNanopubPlaceholder(IRI iri) {
477
        iri = transform(iri);
12✔
478
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.ROOT_NANOPUB_PLACEHOLDER);
51✔
479
    }
480

481
    /**
482
     * Checks if the IRI is a placeholder of any type.
483
     *
484
     * @param iri the IRI to check.
485
     * @return true if the IRI is a placeholder, false otherwise.
486
     */
487
    public boolean isPlaceholder(IRI iri) {
488
        iri = transform(iri);
12✔
489
        if (!typeMap.containsKey(iri)) return false;
15!
490
        for (IRI t : typeMap.get(iri)) {
42!
491
            if (t.equals(NTEMPLATE.VALUE_PLACEHOLDER)) return true;
12!
492
            if (t.equals(NTEMPLATE.URI_PLACEHOLDER)) return true;
12!
493
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
12!
494
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
12!
495
            if (t.equals(NTEMPLATE.AUTO_ESCAPE_URI_PLACEHOLDER)) return true;
12!
496
            if (t.equals(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER)) return true;
12!
497
            if (t.equals(NTEMPLATE.GUIDED_CHOICE_PLACEHOLDER)) return true;
12!
498
            if (t.equals(NTEMPLATE.AGENT_PLACEHOLDER)) return true;
12!
499
            if (t.equals(NTEMPLATE.LITERAL_PLACEHOLDER)) return true;
12!
500
            if (t.equals(NTEMPLATE.LONG_LITERAL_PLACEHOLDER)) return true;
12!
501
            if (t.equals(NTEMPLATE.SEQUENCE_ELEMENT_PLACEHOLDER)) return true;
12!
502
            if (t.equals(NTEMPLATE.ROOT_NANOPUB_PLACEHOLDER)) return true;
18!
503
        }
×
504
        return false;
×
505
    }
506

507
    /**
508
     * Checks if the IRI is an optional statement.
509
     *
510
     * @param iri the IRI to check.
511
     * @return true if the IRI is an optional statement, false otherwise.
512
     */
513
    public boolean isOptionalStatement(IRI iri) {
514
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.OPTIONAL_STATEMENT);
×
515
    }
516

517
    /**
518
     * Checks if the IRI is an advanced statement, which are only show upon expanding to full view in form mode.
519
     *
520
     * @param iri the IRI to check.
521
     * @return true if the IRI is an advanced statement, false otherwise.
522
     */
523
    public boolean isAdvancedStatement(IRI iri) {
524
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(ADVANCED_STATEMENT);
×
525
    }
526

527
    /**
528
     * Checks if the IRI is a grouped statement.
529
     *
530
     * @param iri the IRI to check.
531
     * @return true if the IRI is a grouped statement, false otherwise.
532
     */
533
    public boolean isGroupedStatement(IRI iri) {
534
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.GROUPED_STATEMENT);
×
535
    }
536

537
    /**
538
     * Checks if the IRI is a repeatable statement.
539
     *
540
     * @param iri the IRI to check.
541
     * @return true if the IRI is a repeatable statement, false otherwise.
542
     */
543
    public boolean isRepeatableStatement(IRI iri) {
544
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.REPEATABLE_STATEMENT);
×
545
    }
546

547
    /**
548
     * Returns the possible values for a given IRI.
549
     *
550
     * @param iri the IRI for which to get possible values.
551
     * @return a list of possible values for the IRI. If no values are found, an empty list is returned.
552
     */
553
    public List<Value> getPossibleValues(IRI iri) {
554
        iri = transform(iri);
×
555
        List<Value> l = possibleValueMap.get(iri);
×
556
        if (l == null) {
×
557
            l = new ArrayList<>();
×
558
            possibleValueMap.put(iri, l);
×
559
        }
560
        List<IRI> nanopubList = possibleValuesToLoadMap.get(iri);
×
561
        if (nanopubList != null) {
×
562
            for (IRI npIri : new ArrayList<>(nanopubList)) {
×
563
                try {
564
                    Nanopub valuesNanopub = Utils.getNanopub(npIri.stringValue());
×
565
                    for (Statement st : valuesNanopub.getAssertion()) {
×
566
                        if (st.getPredicate().equals(RDFS.LABEL)) {
×
567
                            l.add((IRI) st.getSubject());
×
568
                        }
569
                    }
×
570
                    nanopubList.remove(npIri);
×
571
                } catch (Exception ex) {
×
572
                    logger.error("Could not load possible values from nanopub {}", npIri.stringValue(), ex);
×
573
                }
×
574
            }
×
575
        }
576
        return l;
×
577
    }
578

579
    /**
580
     * Returns the IRI of the default provenance for the template.
581
     *
582
     * @return the IRI of the default provenance, or null if not set.
583
     */
584
    public IRI getDefaultProvenance() {
585
        return defaultProvenance;
×
586
    }
587

588
    /**
589
     * Returns the target namespace for the template.
590
     *
591
     * @return the target namespace as a string, or null if not set.
592
     */
593
    public String getTargetNamespace() {
594
        return targetNamespace;
×
595
    }
596

597
    /**
598
     * Returns the nanopub label pattern.
599
     *
600
     * @return the nanopub label pattern as a string, or null if not set.
601
     */
602
    public String getNanopubLabelPattern() {
603
        return nanopubLabelPattern;
9✔
604
    }
605

606
    /**
607
     * Returns the list of target nanopub types.
608
     *
609
     * @return a list of IRI objects representing the target nanopub types.
610
     */
611
    public List<IRI> getTargetNanopubTypes() {
612
        return targetNanopubTypes;
×
613
    }
614

615
    /**
616
     * Returns the list of the required pubInfo elements for the template.
617
     *
618
     * @return a list of IRI objects representing the required pubInfo elements.
619
     */
620
    public List<IRI> getRequiredPubInfoElements() {
621
        return requiredPubInfoElements;
×
622
    }
623

624
    /**
625
     * Returns the possible values from an API for a given IRI and search term.
626
     *
627
     * @param iri        the IRI for which to get possible values from the API.
628
     * @param searchTerm the search term to filter the possible values.
629
     * @param labelMap   a map to store labels for the possible values.
630
     * @return a list of possible values from the API, filtered by the search term.
631
     */
632
    public List<String> getPossibleValuesFromApi(IRI iri, String searchTerm, Map<String, String> labelMap) {
633
        iri = transform(iri);
12✔
634
        List<String> values = new ArrayList<>();
12✔
635
        List<String> apiList = apiMap.get(iri);
18✔
636
        if (apiList != null) {
6!
637
            for (String apiString : apiList) {
30✔
638
                LookupApis.getPossibleValues(apiString, searchTerm, labelMap, values);
15✔
639
            }
3✔
640
        }
641
        return values;
6✔
642
    }
643

644
    /**
645
     * Returns the tag associated with the template.
646
     *
647
     * @return the tag as a string, or null if not set.
648
     */
649
    public String getTag() {
650
        return tag;
9✔
651
    }
652

653
    private void processTemplate(Nanopub templateNp) throws MalformedTemplateException {
654
        boolean isNpTemplate = false;
6✔
655
        for (Statement st : templateNp.getAssertion()) {
33✔
656
            if (st.getSubject().equals(templateNp.getAssertionUri()) && st.getPredicate().equals(RDF.TYPE)) {
33!
657
                if (st.getObject().equals(NTEMPLATE.ASSERTION_TEMPLATE) || st.getObject().equals(NTEMPLATE.PROVENANCE_TEMPLATE) || st.getObject().equals(NTEMPLATE.PUBINFO_TEMPLATE)) {
15!
658
                    isNpTemplate = true;
6✔
659
                    break;
3✔
660
                }
661
            }
662
        }
3✔
663

664
        if (isNpTemplate) {
6✔
665
            processNpTemplate(templateNp);
12✔
666
        } else {
667
            // Experimental SHACL-based template:
668
            processShaclTemplate(templateNp);
9✔
669
        }
670
    }
3✔
671

672
    private void processNpTemplate(Nanopub templateNp) {
673
        templateIri = templateNp.getAssertionUri();
12✔
674
        for (Statement st : templateNp.getAssertion()) {
33✔
675
            final IRI subj = (IRI) st.getSubject();
12✔
676
            final IRI pred = st.getPredicate();
9✔
677
            final Value obj = st.getObject();
9✔
678
            final String objS = obj.stringValue();
9✔
679

680
            if (subj.equals(templateIri)) {
15✔
681
                if (pred.equals(RDFS.LABEL)) {
12✔
682
                    label = objS;
12✔
683
                } else if (pred.equals(DCTERMS.DESCRIPTION)) {
12✔
684
                    description = Utils.sanitizeHtml(objS);
15✔
685
                } else if (obj instanceof IRI objIri) {
18✔
686
                    if (pred.equals(NTEMPLATE.HAS_DEFAULT_PROVENANCE)) {
12✔
687
                        defaultProvenance = objIri;
12✔
688
                    } else if (pred.equals(NTEMPLATE.HAS_REQUIRED_PUBINFO_ELEMENT)) {
12✔
689
                        requiredPubInfoElements.add(objIri);
18✔
690
                    } else if (pred.equals(NTEMPLATE.HAS_TARGET_NAMESPACE)) {
12✔
691
                        targetNamespace = objS;
12✔
692
                    } else if (pred.equals(NTEMPLATE.HAS_TARGET_NANOPUB_TYPE)) {
12✔
693
                        targetNanopubTypes.add(objIri);
18✔
694
                    }
695
                } else if (obj instanceof Literal) {
9!
696
                    if (pred.equals(NTEMPLATE.HAS_TAG)) {
12✔
697
                        // TODO This should be replaced at some point with a more sophisticated mechanism based on classes.
698
                        // We are assuming that there is at most one tag.
699
                        this.tag = objS;
12✔
700
                    } else if (pred.equals(NTEMPLATE.HAS_NANOPUB_LABEL_PATTERN)) {
12!
701
                        nanopubLabelPattern = objS;
9✔
702
                    }
703
                }
704
            }
705
            if (pred.equals(RDF.TYPE) && obj instanceof IRI objIri) {
30!
706
                addType(subj, objIri);
15✔
707
            } else if (pred.equals(NTEMPLATE.HAS_STATEMENT) && obj instanceof IRI objIri) {
30!
708
                List<IRI> l = statementMap.get(subj);
18✔
709
                if (l == null) {
6✔
710
                    l = new ArrayList<>();
12✔
711
                    statementMap.put(subj, l);
18✔
712
                }
713
                l.add((IRI) objIri);
12✔
714
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUE)) {
15✔
715
                List<Value> l = possibleValueMap.get(subj);
18✔
716
                if (l == null) {
6✔
717
                    l = new ArrayList<>();
12✔
718
                    possibleValueMap.put(subj, l);
18✔
719
                }
720
                l.add(obj);
12✔
721
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUES_FROM)) {
15✔
722
                List<IRI> l = possibleValuesToLoadMap.get(subj);
18✔
723
                if (l == null) {
6!
724
                    l = new ArrayList<>();
12✔
725
                    possibleValuesToLoadMap.put(subj, l);
18✔
726
                }
727
                if (obj instanceof IRI objIri) {
18!
728
                    l.add(objIri);
12✔
729
                    Nanopub valuesNanopub = Utils.getNanopub(objS);
9✔
730
                    for (Statement s : valuesNanopub.getAssertion()) {
33✔
731
                        if (s.getPredicate().equals(RDFS.LABEL)) {
15!
732
                            labelMap.put((IRI) s.getSubject(), s.getObject().stringValue());
30✔
733
                        }
734
                    }
3✔
735
                }
736
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUES_FROM_API)) {
15✔
737
                List<String> l = apiMap.get(subj);
18✔
738
                if (l == null) {
6✔
739
                    l = new ArrayList<>();
12✔
740
                    apiMap.put(subj, l);
18✔
741
                }
742
                if (obj instanceof Literal) {
9!
743
                    l.add(objS);
12✔
744
                }
745
            } else if (pred.equals(RDFS.LABEL) && obj instanceof Literal) {
24!
746
                labelMap.put(subj, objS);
21✔
747
            } else if (pred.equals(NTEMPLATE.HAS_DATATYPE) && obj instanceof IRI objIri) {
30!
748
                datatypeMap.put(subj, objIri);
21✔
749
            } else if (pred.equals(NTEMPLATE.HAS_LANGUAGE_TAG) && obj instanceof Literal) {
21!
750
                languageTagMap.put(subj, Literals.normalizeLanguageTag(objS));
24✔
751
            } else if (pred.equals(NTEMPLATE.HAS_PREFIX) && obj instanceof Literal) {
21!
752
                prefixMap.put(subj, objS);
21✔
753
            } else if (pred.equals(NTEMPLATE.HAS_PREFIX_LABEL) && obj instanceof Literal) {
21!
754
                prefixLabelMap.put(subj, objS);
21✔
755
            } else if (pred.equals(NTEMPLATE.HAS_REGEX) && obj instanceof Literal) {
21!
756
                regexMap.put(subj, objS);
21✔
757
            } else if (pred.equals(RDF.SUBJECT) && obj instanceof IRI objIri) {
30!
758
                statementSubjects.put(subj, objIri);
21✔
759
            } else if (pred.equals(RDF.PREDICATE) && obj instanceof IRI objIri) {
30!
760
                statementPredicates.put(subj, objIri);
21✔
761
            } else if (pred.equals(RDF.OBJECT)) {
12✔
762
                statementObjects.put(subj, obj);
21✔
763
            } else if (pred.equals(NTEMPLATE.HAS_DEFAULT_VALUE)) {
12✔
764
                defaultValues.put(subj, obj);
21✔
765
            } else if (pred.equals(NTEMPLATE.STATEMENT_ORDER)) {
12✔
766
                if (obj instanceof Literal && objS.matches("[0-9]+")) {
21!
767
                    statementOrder.put(subj, Integer.valueOf(objS));
21✔
768
                }
769
            }
770
        }
3✔
771
//                List<IRI> assertionTypes = typeMap.get(templateIri);
772
//                if (assertionTypes == null || (!assertionTypes.contains(NTEMPLATE.ASSERTION_TEMPLATE) &&
773
//                                !assertionTypes.contains(NTEMPLATE.PROVENANCE_TEMPLATE) && !assertionTypes.contains(PUBINFO_TEMPLATE))) {
774
//                        throw new MalformedTemplateException("Unknown template type");
775
//                }
776
        // Grouped IRI are added via group, so direct link from template is redundant:
777
        for (IRI iri : typeMap.keySet()) {
36✔
778
            if (!typeMap.get(iri).contains(NTEMPLATE.GROUPED_STATEMENT)) continue;
27✔
779
            for (IRI groupedIri : getStatementIris(iri)) {
36✔
780
                statementMap.get(templateIri).remove(groupedIri);
27✔
781
            }
3✔
782
        }
3✔
783
        for (List<IRI> l : statementMap.values()) {
36✔
784
            l.sort(statementComparator);
12✔
785
        }
3✔
786
    }
3✔
787

788
    private void processShaclTemplate(Nanopub templateNp) throws MalformedTemplateException {
789
        templateIri = null;
9✔
790
        for (Statement st : templateNp.getAssertion()) {
33!
791
            if (st.getPredicate().equals(SHACL.TARGET_CLASS)) {
15✔
792
                templateIri = (IRI) st.getSubject();
15✔
793
                break;
3✔
794
            }
795
        }
3✔
796
        if (templateIri == null) {
9!
797
            throw new MalformedTemplateException("Base node shape not found");
×
798
        }
799

800
        IRI baseSubj = vf.createIRI(templateIri.stringValue() + "+subj");
21✔
801
        addType(baseSubj, NTEMPLATE.INTRODUCED_RESOURCE);
12✔
802

803
        List<IRI> statementList = new ArrayList<>();
12✔
804
        Map<IRI, Integer> minCounts = new HashMap<>();
12✔
805
        Map<IRI, Integer> maxCounts = new HashMap<>();
12✔
806

807
        for (Statement st : templateNp.getAssertion()) {
33✔
808
            final IRI subj = (IRI) st.getSubject();
12✔
809
            final IRI pred = st.getPredicate();
9✔
810
            final Value obj = st.getObject();
9✔
811
            final String objS = obj.stringValue();
9✔
812

813
            if (subj.equals(templateIri)) {
15✔
814
                if (pred.equals(RDFS.LABEL)) {
12!
815
                    label = objS;
×
816
                } else if (pred.equals(DCTERMS.DESCRIPTION)) {
12!
817
                    description = Utils.sanitizeHtml(objS);
×
818
                } else if (obj instanceof IRI objIri) {
18✔
819
                    if (pred.equals(NTEMPLATE.HAS_DEFAULT_PROVENANCE)) {
12!
820
                        defaultProvenance = objIri;
×
821
                    } else if (pred.equals(NTEMPLATE.HAS_REQUIRED_PUBINFO_ELEMENT)) {
12!
822
                        requiredPubInfoElements.add(objIri);
×
823
                    } else if (pred.equals(NTEMPLATE.HAS_TARGET_NAMESPACE)) {
12!
824
                        targetNamespace = objS;
×
825
                    } else if (pred.equals(NTEMPLATE.HAS_TARGET_NANOPUB_TYPE)) {
12!
826
                        targetNanopubTypes.add(objIri);
×
827
                    }
828
                } else if (obj instanceof Literal) {
9!
829
                    if (pred.equals(NTEMPLATE.HAS_TAG)) {
12!
830
                        // TODO This should be replaced at some point with a more sophisticated mechanism based on classes.
831
                        // We are assuming that there is at most one tag.
832
                        this.tag = objS;
×
833
                    } else if (pred.equals(NTEMPLATE.HAS_NANOPUB_LABEL_PATTERN)) {
12!
834
                        nanopubLabelPattern = objS;
×
835
                    }
836
                }
837
            }
838
            if (pred.equals(RDF.TYPE) && obj instanceof IRI objIri) {
30!
839
                addType(subj, objIri);
15✔
840
            } else if (pred.equals(SHACL.PROPERTY) && obj instanceof IRI objIri) {
30!
841
                statementList.add(objIri);
12✔
842
                List<IRI> l = statementMap.get(subj);
18✔
843
                if (l == null) {
6✔
844
                    l = new ArrayList<>();
12✔
845
                    statementMap.put(subj, l);
18✔
846
                }
847
                l.add((IRI) objIri);
12✔
848
                IRI stSubjIri = vf.createIRI(subj.stringValue() + "+subj");
18✔
849
                statementSubjects.put(objIri, stSubjIri);
18✔
850
                addType(stSubjIri, NTEMPLATE.LOCAL_RESOURCE);
12✔
851
                addType(stSubjIri, NTEMPLATE.URI_PLACEHOLDER);
12✔
852
            } else if (pred.equals(SHACL.PATH) && obj instanceof IRI objIri) {
33!
853
                statementPredicates.put(subj, objIri);
21✔
854
            } else if (pred.equals(SHACL.HAS_VALUE) && obj instanceof IRI objIri) {
30!
855
                statementObjects.put(subj, objIri);
21✔
856
            } else if (pred.equals(SHACL.TARGET_CLASS) && obj instanceof IRI objIri) {
30!
857
                IRI stIri = vf.createIRI(templateNp.getUri() + "/$type");
21✔
858
                statementList.add(stIri);
12✔
859
                List<IRI> l = statementMap.get(subj);
18✔
860
                if (l == null) {
6✔
861
                    l = new ArrayList<>();
12✔
862
                    statementMap.put(subj, l);
18✔
863
                }
864
                l.add((IRI) stIri);
12✔
865
                statementSubjects.put(stIri, baseSubj);
18✔
866
                statementPredicates.put(stIri, RDF.TYPE);
18✔
867
                statementObjects.put(stIri, objIri);
18✔
868
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUE)) {
15!
869
                List<Value> l = possibleValueMap.get(subj);
×
870
                if (l == null) {
×
871
                    l = new ArrayList<>();
×
872
                    possibleValueMap.put(subj, l);
×
873
                }
874
                l.add(obj);
×
875
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUES_FROM)) {
12!
876
                List<IRI> l = possibleValuesToLoadMap.get(subj);
×
877
                if (l == null) {
×
878
                    l = new ArrayList<>();
×
879
                    possibleValuesToLoadMap.put(subj, l);
×
880
                }
881
                if (obj instanceof IRI objIri) {
×
882
                    l.add(objIri);
×
883
                    Nanopub valuesNanopub = Utils.getNanopub(objS);
×
884
                    for (Statement s : valuesNanopub.getAssertion()) {
×
885
                        if (s.getPredicate().equals(RDFS.LABEL)) {
×
886
                            labelMap.put((IRI) s.getSubject(), s.getObject().stringValue());
×
887
                        }
888
                    }
×
889
                }
890
            } else if (pred.equals(NTEMPLATE.POSSIBLE_VALUES_FROM_API)) {
12!
891
                List<String> l = apiMap.get(subj);
×
892
                if (l == null) {
×
893
                    l = new ArrayList<>();
×
894
                    apiMap.put(subj, l);
×
895
                }
896
                if (obj instanceof Literal) {
×
897
                    l.add(objS);
×
898
                }
899
            } else if (pred.equals(RDFS.LABEL) && obj instanceof Literal) {
21!
900
                labelMap.put(subj, objS);
21✔
901
            } else if (pred.equals(NTEMPLATE.HAS_DATATYPE) && obj instanceof IRI objIri) {
12!
902
                datatypeMap.put(subj, objIri);
×
903
            } else if (pred.equals(NTEMPLATE.HAS_LANGUAGE_TAG) && obj instanceof Literal) {
12!
904
                languageTagMap.put(subj, Literals.normalizeLanguageTag(objS));
×
905
            } else if (pred.equals(NTEMPLATE.HAS_PREFIX) && obj instanceof Literal) {
12!
906
                prefixMap.put(subj, objS);
×
907
            } else if (pred.equals(NTEMPLATE.HAS_PREFIX_LABEL) && obj instanceof Literal) {
12!
908
                prefixLabelMap.put(subj, objS);
×
909
            } else if (pred.equals(NTEMPLATE.HAS_REGEX) && obj instanceof Literal) {
12!
910
                regexMap.put(subj, objS);
×
911
//                        } else if (pred.equals(RDF.SUBJECT) && obj instanceof IRI objIri) {
912
//                                statementSubjects.put(subj, objIri);
913
//                        } else if (pred.equals(RDF.PREDICATE) && obj instanceof IRI objIri) {
914
//                                statementPredicates.put(subj, objIri);
915
//                        } else if (pred.equals(RDF.OBJECT)) {
916
//                                statementObjects.put(subj, obj);
917
            } else if (pred.equals(NTEMPLATE.HAS_DEFAULT_VALUE)) {
12!
918
                defaultValues.put(subj, obj);
×
919
            } else if (pred.equals(NTEMPLATE.STATEMENT_ORDER)) {
12!
920
                if (obj instanceof Literal && objS.matches("[0-9]+")) {
×
921
                    statementOrder.put(subj, Integer.valueOf(objS));
×
922
                }
923
            } else if (pred.equals(SHACL.MIN_COUNT)) {
12✔
924
                try {
925
                    minCounts.put(subj, Integer.parseInt(obj.stringValue()));
24✔
926
                } catch (NumberFormatException ex) {
×
927
                    logger.error("Could not parse <{}> value: {}", SHACL.MIN_COUNT, obj.stringValue());
×
928
                }
3✔
929
            } else if (pred.equals(SHACL.MAX_COUNT)) {
12✔
930
                try {
931
                    maxCounts.put(subj, Integer.parseInt(obj.stringValue()));
24✔
932
                } catch (NumberFormatException ex) {
×
933
                    logger.error("Could not parse <{}> value: {}", SHACL.MAX_COUNT, obj.stringValue());
×
934
                }
3✔
935
            }
936
        }
3✔
937
        for (List<IRI> l : statementMap.values()) {
36✔
938
            l.sort(statementComparator);
12✔
939
        }
3✔
940
        for (IRI iri : statementList) {
30✔
941
            if (!statementObjects.containsKey(iri)) {
15✔
942
                IRI stObjIri = vf.createIRI(iri.stringValue() + "+obj");
18✔
943
                statementObjects.put(iri, stObjIri);
18✔
944
                addType(stObjIri, NTEMPLATE.VALUE_PLACEHOLDER);
12✔
945
                if (!minCounts.containsKey(iri) || minCounts.get(iri) <= 0) {
30✔
946
                    addType(iri, NTEMPLATE.OPTIONAL_STATEMENT);
12✔
947
                }
948
                if (!maxCounts.containsKey(iri) || maxCounts.get(iri) > 1) {
33!
949
                    addType(iri, NTEMPLATE.REPEATABLE_STATEMENT);
12✔
950
                }
951
            }
952
        }
3✔
953
        if (!labelMap.containsKey(baseSubj) && typeMap.get(baseSubj).contains(NTEMPLATE.URI_PLACEHOLDER) && typeMap.get(baseSubj).contains(NTEMPLATE.LOCAL_RESOURCE)) {
63!
954
            labelMap.put(baseSubj, "short ID as URI suffix");
18✔
955
        }
956

957
        if (label == null) {
9!
958
            label = NanopubUtils.getLabel(templateNp);
12✔
959
        }
960
    }
3✔
961

962
    public void addToLabelMap(Object key, String label) {
963
        if (key instanceof IRI iri) {
×
964
            labelMap.put(iri, label);
×
965
        } else {
966
            labelMap.put(vf.createIRI(key.toString()), label);
×
967
        }
968
    }
×
969

970
    private void addType(IRI thing, IRI type) {
971
        List<IRI> l = typeMap.get(thing);
18✔
972
        if (l == null) {
6✔
973
            l = new ArrayList<>();
12✔
974
            typeMap.put(thing, l);
18✔
975
        }
976
        l.add(type);
12✔
977
    }
3✔
978

979
    private IRI transform(IRI iri) {
980
        if (iri.stringValue().matches(".*__[0-9]+")) {
15!
981
            // TODO: Check that this double-underscore pattern isn't used otherwise:
982
            return vf.createIRI(iri.stringValue().replaceFirst("__[0-9]+$", ""));
×
983
        }
984
        return iri;
6✔
985
    }
986

987
    private StatementComparator statementComparator = new StatementComparator();
18✔
988

989
    private class StatementComparator implements Comparator<IRI>, Serializable {
18✔
990

991
        /**
992
         * Compares two IRIs based on their order in the template.
993
         *
994
         * @param arg0 the first object to be compared.
995
         * @param arg1 the second object to be compared.
996
         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
997
         */
998
        @Override
999
        public int compare(IRI arg0, IRI arg1) {
1000
            Integer i0 = statementOrder.get(arg0);
21✔
1001
            Integer i1 = statementOrder.get(arg1);
21✔
1002
            if (i0 == null && i1 == null) return arg0.stringValue().compareTo(arg1.stringValue());
30✔
1003
            if (i0 == null) return 1;
12✔
1004
            if (i1 == null) return -1;
12✔
1005
            return i0 - i1;
18✔
1006
        }
1007

1008
    }
1009

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