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

knowledgepixels / nanodash / 21406524015

27 Jan 2026 05:02PM UTC coverage: 15.111% (+0.8%) from 14.27%
21406524015

push

github

tkuhn
feat: Support for "advanced" statements, hidden in collapsed view

590 of 5080 branches covered (11.61%)

Branch coverage included in aggregate %.

1584 of 9307 relevant lines covered (17.02%)

2.21 hits per line

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

23.51
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
                int repeatSuffix = Integer.parseInt(iri.stringValue().replaceFirst("^.*__([0-9]+)$", "$1"));
×
241
                return vf.createIRI(vIri.stringValue() + (repeatSuffix + 1));
×
242
            }
243
        }
244
        iri = transform(iri);
×
245
        return defaultValues.get(iri);
×
246
    }
247

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

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

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

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

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

297
    /**
298
     * Checks if the template is a local resource.
299
     *
300
     * @param iri the IRI to check.
301
     * @return true if the IRI is a local resource, false otherwise.
302
     */
303
    public boolean isLocalResource(IRI iri) {
304
        iri = transform(iri);
×
305
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.LOCAL_RESOURCE);
×
306
    }
307

308
    /**
309
     * Checks if the template is an introduced resource.
310
     *
311
     * @param iri the IRI to check.
312
     * @return true if the IRI is an introduced resource, false otherwise.
313
     */
314
    public boolean isIntroducedResource(IRI iri) {
315
        iri = transform(iri);
×
316
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.INTRODUCED_RESOURCE);
×
317
    }
318

319
    /**
320
     * Checks if the template is an embedded resource.
321
     *
322
     * @param iri the IRI to check.
323
     * @return true if the IRI is an embedded resource, false otherwise.
324
     */
325
    public boolean isEmbeddedResource(IRI iri) {
326
        iri = transform(iri);
×
327
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.EMBEDDED_RESOURCE);
×
328
    }
329

330
    /**
331
     * Checks if the IRI is a value placeholder.
332
     *
333
     * @param iri the IRI to check.
334
     * @return true if the IRI is a value placeholder, false otherwise.
335
     */
336
    public boolean isValuePlaceholder(IRI iri) {
337
        iri = transform(iri);
×
338
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.VALUE_PLACEHOLDER);
×
339
    }
340

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

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

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

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

400
    /**
401
     * Checks if the IRI is a literal placeholder.
402
     *
403
     * @param iri the IRI to check.
404
     * @return true if the IRI is a literal placeholder, false otherwise.
405
     */
406
    public boolean isLiteralPlaceholder(IRI iri) {
407
        iri = transform(iri);
×
408
        return typeMap.containsKey(iri) && (typeMap.get(iri).contains(NTEMPLATE.LITERAL_PLACEHOLDER) || typeMap.get(iri).contains(NTEMPLATE.LONG_LITERAL_PLACEHOLDER));
×
409
    }
410

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

422
    /**
423
     * Checks if the IRI is a restricted choice placeholder.
424
     *
425
     * @param iri the IRI to check.
426
     * @return true if the IRI is a restricted choice placeholder, false otherwise.
427
     */
428
    public boolean isRestrictedChoicePlaceholder(IRI iri) {
429
        iri = transform(iri);
×
430
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER);
×
431
    }
432

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

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

455
    /**
456
     * Checks if the IRI is a sequence element placeholder.
457
     *
458
     * @param iri the IRI to check.
459
     * @return true if the IRI is a sequence element placeholder, false otherwise.
460
     */
461
    public boolean isSequenceElementPlaceholder(IRI iri) {
462
        iri = transform(iri);
×
463
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.SEQUENCE_ELEMENT_PLACEHOLDER);
×
464
    }
465

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

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

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

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

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

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

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

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

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

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

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

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

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

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

648
        if (isNpTemplate) {
6!
649
            processNpTemplate(templateNp);
12✔
650
        } else {
651
            // Experimental SHACL-based template:
652
            processShaclTemplate(templateNp);
×
653
        }
654
    }
3✔
655

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

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

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

784
        IRI baseSubj = vf.createIRI(templateIri.stringValue() + "+subj");
×
785
        addType(baseSubj, NTEMPLATE.INTRODUCED_RESOURCE);
×
786

787
        List<IRI> statementList = new ArrayList<>();
×
788
        Map<IRI, Integer> minCounts = new HashMap<>();
×
789
        Map<IRI, Integer> maxCounts = new HashMap<>();
×
790

791
        for (Statement st : templateNp.getAssertion()) {
×
792
            final IRI subj = (IRI) st.getSubject();
×
793
            final IRI pred = st.getPredicate();
×
794
            final Value obj = st.getObject();
×
795
            final String objS = obj.stringValue();
×
796

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

941
        if (label == null) {
×
942
            label = NanopubUtils.getLabel(templateNp);
×
943
        }
944
    }
×
945

946
    public void addToLabelMap(Object key, String label) {
947
        if (key instanceof IRI iri) {
×
948
            labelMap.put(iri, label);
×
949
        } else {
950
            labelMap.put(vf.createIRI(key.toString()), label);
×
951
        }
952
    }
×
953

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

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

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

980
    private StatementComparator statementComparator = new StatementComparator();
18✔
981

982
    private class StatementComparator implements Comparator<IRI>, Serializable {
18✔
983

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

1001
    }
1002

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