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

knowledgepixels / nanodash / 21518138890

30 Jan 2026 01:52PM UTC coverage: 14.072% (-0.2%) from 14.235%
21518138890

push

github

tkuhn
fix: Only local default values in repeated statements get numbers

543 of 5100 branches covered (10.65%)

Branch coverage included in aggregate %.

1489 of 9340 relevant lines covered (15.94%)

2.11 hits per line

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

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

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

10
import org.eclipse.rdf4j.common.exception.RDF4JException;
11
import org.eclipse.rdf4j.model.IRI;
12
import org.eclipse.rdf4j.model.Literal;
13
import org.eclipse.rdf4j.model.Statement;
14
import org.eclipse.rdf4j.model.Value;
15
import org.eclipse.rdf4j.model.ValueFactory;
16
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
17
import org.eclipse.rdf4j.model.util.Literals;
18
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
19
import org.eclipse.rdf4j.model.vocabulary.RDF;
20
import org.eclipse.rdf4j.model.vocabulary.RDFS;
21
import org.eclipse.rdf4j.model.vocabulary.SHACL;
22
import org.nanopub.Nanopub;
23
import org.nanopub.NanopubUtils;
24
import org.nanopub.vocabulary.NTEMPLATE;
25
import org.slf4j.Logger;
26
import org.slf4j.LoggerFactory;
27

28
import com.knowledgepixels.nanodash.LookupApis;
29
import com.knowledgepixels.nanodash.Utils;
30

31
import net.trustyuri.TrustyUriUtils;
32

33
/**
34
 * Represents a template for creating nanopublications.
35
 */
36
public class Template implements Serializable {
37

38
    private static ValueFactory vf = SimpleValueFactory.getInstance();
6✔
39
    private static final Logger logger = LoggerFactory.getLogger(Template.class);
9✔
40

41
    /**
42
     * Default target namespace for templates.
43
     */
44
    public static final String DEFAULT_TARGET_NAMESPACE = "https://w3id.org/np/";
45

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

49
    private Nanopub nanopub;
50
    private String label;
51
    private String description;
52

53
    // TODO: Make all these maps more generic and the code simpler:
54
    private IRI templateIri;
55
    private Map<IRI, List<IRI>> typeMap = new HashMap<>();
15✔
56
    private Map<IRI, List<Value>> possibleValueMap = new HashMap<>();
15✔
57
    private Map<IRI, List<IRI>> possibleValuesToLoadMap = new HashMap<>();
15✔
58
    private Map<IRI, List<String>> apiMap = new HashMap<>();
15✔
59
    private Map<IRI, String> labelMap = new HashMap<>();
15✔
60
    private Map<IRI, IRI> datatypeMap = new HashMap<>();
15✔
61
    private Map<IRI, String> languageTagMap = new HashMap<>();
15✔
62
    private Map<IRI, String> prefixMap = new HashMap<>();
15✔
63
    private Map<IRI, String> prefixLabelMap = new HashMap<>();
15✔
64
    private Map<IRI, String> regexMap = new HashMap<>();
15✔
65
    private Map<IRI, List<IRI>> statementMap = new HashMap<>();
15✔
66
    private Map<IRI, IRI> statementSubjects = new HashMap<>();
15✔
67
    private Map<IRI, IRI> statementPredicates = new HashMap<>();
15✔
68
    private Map<IRI, Value> statementObjects = new HashMap<>();
15✔
69
    private Map<IRI, Integer> statementOrder = new HashMap<>();
15✔
70
    private IRI defaultProvenance;
71
    private List<IRI> requiredPubinfoElements = new ArrayList<>();
15✔
72
    private String tag = null;
9✔
73
    private Map<IRI, Value> defaultValues = new HashMap<>();
15✔
74
    private String targetNamespace = null;
9✔
75
    private String nanopubLabelPattern;
76
    private List<IRI> targetNanopubTypes = new ArrayList<>();
15✔
77

78
    /**
79
     * Creates a Template object from a template id.
80
     *
81
     * @param templateId the id of the template, which is the URI of a nanopublication that contains the template definition.
82
     * @throws RDF4JException             if there is an error retrieving the nanopublication.
83
     * @throws MalformedTemplateException if the template is malformed or not a valid nanopub template.
84
     */
85
    Template(String templateId) throws RDF4JException, MalformedTemplateException {
6✔
86
        nanopub = Utils.getNanopub(templateId);
12✔
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 getFirstOccurence(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);
×
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);
×
309
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.LOCAL_RESOURCE);
×
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);
×
320
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.INTRODUCED_RESOURCE);
×
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);
×
331
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.EMBEDDED_RESOURCE);
×
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);
×
342
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.VALUE_PLACEHOLDER);
×
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);
×
353
        if (!typeMap.containsKey(iri)) return false;
×
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);
×
412
        return typeMap.containsKey(iri) && (typeMap.get(iri).contains(NTEMPLATE.LITERAL_PLACEHOLDER) || typeMap.get(iri).contains(NTEMPLATE.LONG_LITERAL_PLACEHOLDER));
×
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);
×
434
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER);
×
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);
×
467
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.SEQUENCE_ELEMENT_PLACEHOLDER);
×
468
    }
469

470
    /**
471
     * Checks if the IRI is a placeholder of any type.
472
     *
473
     * @param iri the IRI to check.
474
     * @return true if the IRI is a placeholder, false otherwise.
475
     */
476
    public boolean isPlaceholder(IRI iri) {
477
        iri = transform(iri);
×
478
        if (!typeMap.containsKey(iri)) return false;
×
479
        for (IRI t : typeMap.get(iri)) {
×
480
            if (t.equals(NTEMPLATE.VALUE_PLACEHOLDER)) return true;
×
481
            if (t.equals(NTEMPLATE.URI_PLACEHOLDER)) return true;
×
482
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
×
483
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
×
484
            if (t.equals(NTEMPLATE.AUTO_ESCAPE_URI_PLACEHOLDER)) return true;
×
485
            if (t.equals(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER)) return true;
×
486
            if (t.equals(NTEMPLATE.GUIDED_CHOICE_PLACEHOLDER)) return true;
×
487
            if (t.equals(NTEMPLATE.AGENT_PLACEHOLDER)) return true;
×
488
            if (t.equals(NTEMPLATE.LITERAL_PLACEHOLDER)) return true;
×
489
            if (t.equals(NTEMPLATE.LONG_LITERAL_PLACEHOLDER)) return true;
×
490
            if (t.equals(NTEMPLATE.SEQUENCE_ELEMENT_PLACEHOLDER)) return true;
×
491
        }
×
492
        return false;
×
493
    }
494

495
    /**
496
     * Checks if the IRI is an optional statement.
497
     *
498
     * @param iri the IRI to check.
499
     * @return true if the IRI is an optional statement, false otherwise.
500
     */
501
    public boolean isOptionalStatement(IRI iri) {
502
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.OPTIONAL_STATEMENT);
×
503
    }
504

505
    /**
506
     * Checks if the IRI is an advanced statement, which are only show upon expanding to full view in form mode.
507
     *
508
     * @param iri the IRI to check.
509
     * @return true if the IRI is an advanced statement, false otherwise.
510
     */
511
    public boolean isAdvancedStatement(IRI iri) {
512
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(ADVANCED_STATEMENT);
×
513
    }
514

515
    /**
516
     * Checks if the IRI is a grouped statement.
517
     *
518
     * @param iri the IRI to check.
519
     * @return true if the IRI is a grouped statement, false otherwise.
520
     */
521
    public boolean isGroupedStatement(IRI iri) {
522
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.GROUPED_STATEMENT);
×
523
    }
524

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

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

567
    /**
568
     * Returns the IRI of the default provenance for the template.
569
     *
570
     * @return the IRI of the default provenance, or null if not set.
571
     */
572
    public IRI getDefaultProvenance() {
573
        return defaultProvenance;
×
574
    }
575

576
    /**
577
     * Returns the target namespace for the template.
578
     *
579
     * @return the target namespace as a string, or null if not set.
580
     */
581
    public String getTargetNamespace() {
582
        return targetNamespace;
×
583
    }
584

585
    /**
586
     * Returns the nanopub label pattern.
587
     *
588
     * @return the nanopub label pattern as a string, or null if not set.
589
     */
590
    public String getNanopubLabelPattern() {
591
        return nanopubLabelPattern;
9✔
592
    }
593

594
    /**
595
     * Returns the list of target nanopub types.
596
     *
597
     * @return a list of IRI objects representing the target nanopub types.
598
     */
599
    public List<IRI> getTargetNanopubTypes() {
600
        return targetNanopubTypes;
×
601
    }
602

603
    /**
604
     * Returns the list of the required pubinfo elements for the template.
605
     *
606
     * @return a list of IRI objects representing the required pubinfo elements.
607
     */
608
    public List<IRI> getRequiredPubinfoElements() {
609
        return requiredPubinfoElements;
×
610
    }
611

612
    /**
613
     * Returns the possible values from an API for a given IRI and search term.
614
     *
615
     * @param iri        the IRI for which to get possible values from the API.
616
     * @param searchterm the search term to filter the possible values.
617
     * @param labelMap   a map to store labels for the possible values.
618
     * @return a list of possible values from the API, filtered by the search term.
619
     */
620
    public List<String> getPossibleValuesFromApi(IRI iri, String searchterm, Map<String, String> labelMap) {
621
        iri = transform(iri);
×
622
        List<String> values = new ArrayList<>();
×
623
        List<String> apiList = apiMap.get(iri);
×
624
        if (apiList != null) {
×
625
            for (String apiString : apiList) {
×
626
                LookupApis.getPossibleValues(apiString, searchterm, labelMap, values);
×
627
            }
×
628
        }
629
        return values;
×
630
    }
631

632
    /**
633
     * Returns the tag associated with the template.
634
     *
635
     * @return the tag as a string, or null if not set.
636
     */
637
    public String getTag() {
638
        return tag;
9✔
639
    }
640

641
    private void processTemplate(Nanopub templateNp) throws MalformedTemplateException {
642
        boolean isNpTemplate = false;
6✔
643
        for (Statement st : templateNp.getAssertion()) {
33!
644
            if (st.getSubject().equals(templateNp.getAssertionUri()) && st.getPredicate().equals(RDF.TYPE)) {
33!
645
                if (st.getObject().equals(NTEMPLATE.ASSERTION_TEMPLATE) || st.getObject().equals(NTEMPLATE.PROVENANCE_TEMPLATE) || st.getObject().equals(NTEMPLATE.PUBINFO_TEMPLATE)) {
15!
646
                    isNpTemplate = true;
6✔
647
                    break;
3✔
648
                }
649
            }
650
        }
3✔
651

652
        if (isNpTemplate) {
6!
653
            processNpTemplate(templateNp);
12✔
654
        } else {
655
            // Experimental SHACL-based template:
656
            processShaclTemplate(templateNp);
×
657
        }
658
    }
3✔
659

660
    private void processNpTemplate(Nanopub templateNp) {
661
        templateIri = templateNp.getAssertionUri();
12✔
662
        for (Statement st : templateNp.getAssertion()) {
33✔
663
            final IRI subj = (IRI) st.getSubject();
12✔
664
            final IRI pred = st.getPredicate();
9✔
665
            final Value obj = st.getObject();
9✔
666
            final String objS = obj.stringValue();
9✔
667

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

776
    private void processShaclTemplate(Nanopub templateNp) throws MalformedTemplateException {
777
        templateIri = null;
×
778
        for (Statement st : templateNp.getAssertion()) {
×
779
            if (st.getPredicate().equals(SHACL.TARGET_CLASS)) {
×
780
                templateIri = (IRI) st.getSubject();
×
781
                break;
×
782
            }
783
        }
×
784
        if (templateIri == null) {
×
785
            throw new MalformedTemplateException("Base node shape not found");
×
786
        }
787

788
        IRI baseSubj = vf.createIRI(templateIri.stringValue() + "+subj");
×
789
        addType(baseSubj, NTEMPLATE.INTRODUCED_RESOURCE);
×
790

791
        List<IRI> statementList = new ArrayList<>();
×
792
        Map<IRI, Integer> minCounts = new HashMap<>();
×
793
        Map<IRI, Integer> maxCounts = new HashMap<>();
×
794

795
        for (Statement st : templateNp.getAssertion()) {
×
796
            final IRI subj = (IRI) st.getSubject();
×
797
            final IRI pred = st.getPredicate();
×
798
            final Value obj = st.getObject();
×
799
            final String objS = obj.stringValue();
×
800

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

945
        if (label == null) {
×
946
            label = NanopubUtils.getLabel(templateNp);
×
947
        }
948
    }
×
949

950
    public void addToLabelMap(Object key, String label) {
951
        if (key instanceof IRI iri) {
×
952
            labelMap.put(iri, label);
×
953
        } else {
954
            labelMap.put(vf.createIRI(key.toString()), label);
×
955
        }
956
    }
×
957

958
    private void addType(IRI thing, IRI type) {
959
        List<IRI> l = typeMap.get(thing);
18✔
960
        if (l == null) {
6✔
961
            l = new ArrayList<>();
12✔
962
            typeMap.put(thing, l);
18✔
963
        }
964
        l.add(type);
12✔
965
    }
3✔
966

967
    private void addStatement(IRI thing, IRI type) {
968
        List<IRI> l = typeMap.get(thing);
×
969
        if (l == null) {
×
970
            l = new ArrayList<>();
×
971
            typeMap.put(thing, l);
×
972
        }
973
        l.add(type);
×
974
    }
×
975

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

984
    private StatementComparator statementComparator = new StatementComparator();
18✔
985

986
    private class StatementComparator implements Comparator<IRI>, Serializable {
18✔
987

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

1005
    }
1006

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