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

knowledgepixels / nanodash / 20261582331

16 Dec 2025 08:36AM UTC coverage: 15.019% (-0.4%) from 15.381%
20261582331

push

github

tkuhn
feat: Add simple script for stress-testing

571 of 4976 branches covered (11.48%)

Branch coverage included in aggregate %.

1542 of 9093 relevant lines covered (16.96%)

2.19 hits per line

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

21.03
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);
12✔
40

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

46
    private Nanopub nanopub;
47
    private String label;
48
    private String description;
49

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

75
    /**
76
     * Creates a Template object from a template id.
77
     *
78
     * @param templateId the id of the template, which is the URI of a nanopublication that contains the template definition.
79
     * @throws RDF4JException             if there is an error retrieving the nanopublication.
80
     * @throws MalformedTemplateException if the template is malformed or not a valid nanopub template.
81
     */
82
    Template(String templateId) throws RDF4JException, MalformedTemplateException {
6✔
83
        nanopub = Utils.getNanopub(templateId);
12✔
84
        processTemplate(nanopub);
12✔
85
    }
3✔
86

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

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

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

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

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

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

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

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

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

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

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

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

226
    /**
227
     * Transforms an IRI by removing the artifact code if it is present.
228
     *
229
     * @param iri the IRI to transform.
230
     * @return the transformed IRI, or the original IRI if no transformation is needed.
231
     */
232
    public Value getDefault(IRI iri) {
233
        if (iri.stringValue().matches(".*__[0-9]+")) {
×
234
            String baseIri = iri.stringValue().replaceFirst("__[0-9]+$", "");
×
235
            Value v = defaultValues.get(vf.createIRI(baseIri));
×
236
            if (v instanceof IRI vIri) {
×
237
                int repeatSuffix = Integer.parseInt(iri.stringValue().replaceFirst("^.*__([0-9]+)$", "$1"));
×
238
                return vf.createIRI(vIri.stringValue() + (repeatSuffix + 1));
×
239
            }
240
        }
241
        iri = transform(iri);
×
242
        return defaultValues.get(iri);
×
243
    }
244

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

498
    /**
499
     * Checks if the IRI is a grouped statement.
500
     *
501
     * @param iri the IRI to check.
502
     * @return true if the IRI is a grouped statement, false otherwise.
503
     */
504
    public boolean isGroupedStatement(IRI iri) {
505
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.GROUPED_STATEMENT);
×
506
    }
507

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

518
    /**
519
     * Returns the possible values for a given IRI.
520
     *
521
     * @param iri the IRI for which to get possible values.
522
     * @return a list of possible values for the IRI. If no values are found, an empty list is returned.
523
     */
524
    public List<Value> getPossibleValues(IRI iri) {
525
        iri = transform(iri);
×
526
        List<Value> l = possibleValueMap.get(iri);
×
527
        if (l == null) {
×
528
            l = new ArrayList<>();
×
529
            possibleValueMap.put(iri, l);
×
530
        }
531
        List<IRI> nanopubList = possibleValuesToLoadMap.get(iri);
×
532
        if (nanopubList != null) {
×
533
            for (IRI npIri : new ArrayList<>(nanopubList)) {
×
534
                try {
535
                    Nanopub valuesNanopub = Utils.getNanopub(npIri.stringValue());
×
536
                    for (Statement st : valuesNanopub.getAssertion()) {
×
537
                        if (st.getPredicate().equals(RDFS.LABEL)) {
×
538
                            l.add((IRI) st.getSubject());
×
539
                        }
540
                    }
×
541
                    nanopubList.remove(npIri);
×
542
                } catch (Exception ex) {
×
543
                    logger.error("Could not load possible values from nanopub {}", npIri.stringValue(), ex);
×
544
                }
×
545
            }
×
546
        }
547
        return l;
×
548
    }
549

550
    /**
551
     * Returns the IRI of the default provenance for the template.
552
     *
553
     * @return the IRI of the default provenance, or null if not set.
554
     */
555
    public IRI getDefaultProvenance() {
556
        return defaultProvenance;
×
557
    }
558

559
    /**
560
     * Returns the target namespace for the template.
561
     *
562
     * @return the target namespace as a string, or null if not set.
563
     */
564
    public String getTargetNamespace() {
565
        return targetNamespace;
×
566
    }
567

568
    /**
569
     * Returns the nanopub label pattern.
570
     *
571
     * @return the nanopub label pattern as a string, or null if not set.
572
     */
573
    public String getNanopubLabelPattern() {
574
        return nanopubLabelPattern;
9✔
575
    }
576

577
    /**
578
     * Returns the list of target nanopub types.
579
     *
580
     * @return a list of IRI objects representing the target nanopub types.
581
     */
582
    public List<IRI> getTargetNanopubTypes() {
583
        return targetNanopubTypes;
×
584
    }
585

586
    /**
587
     * Returns the list of the required pubinfo elements for the template.
588
     *
589
     * @return a list of IRI objects representing the required pubinfo elements.
590
     */
591
    public List<IRI> getRequiredPubinfoElements() {
592
        return requiredPubinfoElements;
×
593
    }
594

595
    /**
596
     * Returns the possible values from an API for a given IRI and search term.
597
     *
598
     * @param iri        the IRI for which to get possible values from the API.
599
     * @param searchterm the search term to filter the possible values.
600
     * @param labelMap   a map to store labels for the possible values.
601
     * @return a list of possible values from the API, filtered by the search term.
602
     */
603
    public List<String> getPossibleValuesFromApi(IRI iri, String searchterm, Map<String, String> labelMap) {
604
        iri = transform(iri);
×
605
        List<String> values = new ArrayList<>();
×
606
        List<String> apiList = apiMap.get(iri);
×
607
        if (apiList != null) {
×
608
            for (String apiString : apiList) {
×
609
                LookupApis.getPossibleValues(apiString, searchterm, labelMap, values);
×
610
            }
×
611
        }
612
        return values;
×
613
    }
614

615
    /**
616
     * Returns the tag associated with the template.
617
     *
618
     * @return the tag as a string, or null if not set.
619
     */
620
    public String getTag() {
621
        return tag;
9✔
622
    }
623

624
    private void processTemplate(Nanopub templateNp) throws MalformedTemplateException {
625
        boolean isNpTemplate = false;
6✔
626
        for (Statement st : templateNp.getAssertion()) {
33!
627
            if (st.getSubject().equals(templateNp.getAssertionUri()) && st.getPredicate().equals(RDF.TYPE)) {
33!
628
                if (st.getObject().equals(NTEMPLATE.ASSERTION_TEMPLATE) || st.getObject().equals(NTEMPLATE.PROVENANCE_TEMPLATE) || st.getObject().equals(NTEMPLATE.PUBINFO_TEMPLATE)) {
15!
629
                    isNpTemplate = true;
6✔
630
                    break;
3✔
631
                }
632
            }
633
        }
3✔
634

635
        if (isNpTemplate) {
6!
636
            processNpTemplate(templateNp);
12✔
637
        } else {
638
            // Experimental SHACL-based template:
639
            processShaclTemplate(templateNp);
×
640
        }
641
    }
3✔
642

643
    private void processNpTemplate(Nanopub templateNp) {
644
        templateIri = templateNp.getAssertionUri();
12✔
645
        for (Statement st : templateNp.getAssertion()) {
33✔
646
            final IRI subj = (IRI) st.getSubject();
12✔
647
            final IRI pred = st.getPredicate();
9✔
648
            final Value obj = st.getObject();
9✔
649
            final String objS = obj.stringValue();
9✔
650

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

752
    private void processShaclTemplate(Nanopub templateNp) throws MalformedTemplateException {
753
        templateIri = null;
×
754
        for (Statement st : templateNp.getAssertion()) {
×
755
            if (st.getPredicate().equals(SHACL.TARGET_CLASS)) {
×
756
                templateIri = (IRI) st.getSubject();
×
757
                break;
×
758
            }
759
        }
×
760
        if (templateIri == null) {
×
761
            throw new MalformedTemplateException("Base node shape not found");
×
762
        }
763

764
        IRI baseSubj = vf.createIRI(templateIri.stringValue() + "+subj");
×
765
        addType(baseSubj, NTEMPLATE.INTRODUCED_RESOURCE);
×
766

767
        List<IRI> statementList = new ArrayList<>();
×
768
        Map<IRI, Integer> minCounts = new HashMap<>();
×
769
        Map<IRI, Integer> maxCounts = new HashMap<>();
×
770

771
        for (Statement st : templateNp.getAssertion()) {
×
772
            final IRI subj = (IRI) st.getSubject();
×
773
            final IRI pred = st.getPredicate();
×
774
            final Value obj = st.getObject();
×
775
            final String objS = obj.stringValue();
×
776

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

921
        if (label == null) {
×
922
            label = NanopubUtils.getLabel(templateNp);
×
923
        }
924
    }
×
925

926
    public void addToLabelMap(Object key, String label) {
927
        if (key instanceof IRI iri) {
×
928
            labelMap.put(iri, label);
×
929
        } else {
930
            labelMap.put(vf.createIRI(key.toString()), label);
×
931
        }
932
    }
×
933

934
    private void addType(IRI thing, IRI type) {
935
        List<IRI> l = typeMap.get(thing);
18✔
936
        if (l == null) {
6✔
937
            l = new ArrayList<>();
12✔
938
            typeMap.put(thing, l);
18✔
939
        }
940
        l.add(type);
12✔
941
    }
3✔
942

943
    private void addStatement(IRI thing, IRI type) {
944
        List<IRI> l = typeMap.get(thing);
×
945
        if (l == null) {
×
946
            l = new ArrayList<>();
×
947
            typeMap.put(thing, l);
×
948
        }
949
        l.add(type);
×
950
    }
×
951

952
    private IRI transform(IRI iri) {
953
        if (iri.stringValue().matches(".*__[0-9]+")) {
×
954
            // TODO: Check that this double-underscore pattern isn't used otherwise:
955
            return vf.createIRI(iri.stringValue().replaceFirst("__[0-9]+$", ""));
×
956
        }
957
        return iri;
×
958
    }
959

960
    private StatementComparator statementComparator = new StatementComparator();
18✔
961

962
    private class StatementComparator implements Comparator<IRI>, Serializable {
18✔
963

964
        /**
965
         * Compares two IRIs based on their order in the template.
966
         *
967
         * @param arg0 the first object to be compared.
968
         * @param arg1 the second object to be compared.
969
         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
970
         */
971
        @Override
972
        public int compare(IRI arg0, IRI arg1) {
973
            Integer i0 = statementOrder.get(arg0);
21✔
974
            Integer i1 = statementOrder.get(arg1);
21✔
975
            if (i0 == null && i1 == null) return arg0.stringValue().compareTo(arg1.stringValue());
30!
976
            if (i0 == null) return 1;
×
977
            if (i1 == null) return -1;
×
978
            return i0 - i1;
×
979
        }
980

981
    }
982

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