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

knowledgepixels / nanodash / 17380144000

01 Sep 2025 02:12PM UTC coverage: 12.03% (+0.05%) from 11.978%
17380144000

push

github

ashleycaselli
refactor: replace printStackTrace with logger.error for better error handling

330 of 3850 branches covered (8.57%)

Branch coverage included in aggregate %.

958 of 6857 relevant lines covered (13.97%)

0.62 hits per line

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

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

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

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

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

28
    private static final long serialVersionUID = 1L;
29

30
    private static ValueFactory vf = SimpleValueFactory.getInstance();
2✔
31
    private static final Logger logger = LoggerFactory.getLogger(Template.class);
4✔
32

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

38
    private Nanopub nanopub;
39
    private String label;
40
    private String description;
41

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

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

79
    /**
80
     * Checks if the template is unlisted.
81
     *
82
     * @return true if the template is unlisted, false otherwise.
83
     */
84
    public boolean isUnlisted() {
85
        return typeMap.get(templateIri).contains(NTEMPLATE.UNLISTED_TEMPLATE);
×
86
    }
87

88
    /**
89
     * Returns the Nanopub object representing the template.
90
     *
91
     * @return the Nanopub object of the template.
92
     */
93
    public Nanopub getNanopub() {
94
        return nanopub;
×
95
    }
96

97
    /**
98
     * Returns the ID of the template, which is the URI of the nanopublication.
99
     *
100
     * @return the ID of the template as a string.
101
     */
102
    public String getId() {
103
        return nanopub.getUri().toString();
×
104
    }
105

106
    /**
107
     * Returns the label of the template.
108
     *
109
     * @return the label of the template, or a default label if not set.
110
     */
111
    public String getLabel() {
112
        if (label == null) {
3!
113
            return "Template " + TrustyUriUtils.getArtifactCode(nanopub.getUri().stringValue()).substring(0, 10);
×
114
        }
115
        return label;
3✔
116
    }
117

118
    /**
119
     * Returns the description of the template.
120
     *
121
     * @return the description of the template, or an empty string if not set.
122
     */
123
    public String getDescription() {
124
        return description;
3✔
125
    }
126

127
    /**
128
     * Returns the IRI of the template.
129
     *
130
     * @param iri the IRI to transform.
131
     * @return the transformed IRI, or the original IRI if no transformation is needed.
132
     */
133
    public String getLabel(IRI iri) {
134
        iri = transform(iri);
×
135
        return labelMap.get(iri);
×
136
    }
137

138
    /**
139
     * Returns the IRI of the template, transforming it if necessary.
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 IRI getFirstOccurence(IRI iri) {
145
        for (IRI i : getStatementIris()) {
×
146
            if (statementMap.containsKey(i)) {
×
147
                // grouped statement
148
                for (IRI g : getStatementIris(i)) {
×
149
                    if (iri.equals(statementSubjects.get(g))) return g;
×
150
                    if (iri.equals(statementPredicates.get(g))) return g;
×
151
                    if (iri.equals(statementObjects.get(g))) return g;
×
152
                }
×
153
            } else {
154
                // non-grouped statement
155
                if (iri.equals(statementSubjects.get(i))) return i;
×
156
                if (iri.equals(statementPredicates.get(i))) return i;
×
157
                if (iri.equals(statementObjects.get(i))) return i;
×
158
            }
159
        }
×
160
        return null;
×
161
    }
162

163
    /**
164
     * Returns the datatype for the given literal placeholder IRI.
165
     *
166
     * @param iri the literal placeholder IRI.
167
     * @return the datatype for the literal.
168
     */
169
    public IRI getDatatype(IRI iri) {
170
        iri = transform(iri);
×
171
        return datatypeMap.get(iri);
×
172
    }
173

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

185
    /**
186
     * Returns the prefix for the given IRI.
187
     *
188
     * @param iri the IRI.
189
     * @return the prefix for the IRI.
190
     */
191
    public String getPrefix(IRI iri) {
192
        iri = transform(iri);
×
193
        return prefixMap.get(iri);
×
194
    }
195

196
    /**
197
     * Returns the prefix label for a given IRI.
198
     *
199
     * @param iri the IRI to get the prefix label for.
200
     * @return the prefix label for the IRI, or null if not found.
201
     */
202
    public String getPrefixLabel(IRI iri) {
203
        iri = transform(iri);
×
204
        return prefixLabelMap.get(iri);
×
205
    }
206

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

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

237
    /**
238
     * Returns the statement IRIs associated with the template.
239
     *
240
     * @return the list of statement IRIs for the template.
241
     */
242
    public List<IRI> getStatementIris() {
243
        return statementMap.get(templateIri);
×
244
    }
245

246
    /**
247
     * Returns the statement IRIs associated with a specific group IRI.
248
     *
249
     * @param groupIri the IRI of the group for which to retrieve statement IRIs.
250
     * @return the list of statement IRIs for the specified group IRI, or null if no statements are associated with that group.
251
     */
252
    public List<IRI> getStatementIris(IRI groupIri) {
253
        return statementMap.get(groupIri);
×
254
    }
255

256
    /**
257
     * Returns the subject, predicate, and object of a statement given its IRI.
258
     *
259
     * @param statementIri the IRI of the statement to retrieve.
260
     * @return the subject, predicate, and object of the statement as a triple.
261
     */
262
    public IRI getSubject(IRI statementIri) {
263
        return statementSubjects.get(statementIri);
×
264
    }
265

266
    /**
267
     * Returns the predicate of a statement given its IRI.
268
     *
269
     * @param statementIri the IRI of the statement to retrieve.
270
     * @return the predicate of the statement, or null if not found.
271
     */
272
    public IRI getPredicate(IRI statementIri) {
273
        return statementPredicates.get(statementIri);
×
274
    }
275

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

286
    /**
287
     * Checks if the template is a local resource.
288
     *
289
     * @param iri the IRI to check.
290
     * @return true if the IRI is a local resource, false otherwise.
291
     */
292
    public boolean isLocalResource(IRI iri) {
293
        iri = transform(iri);
×
294
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.LOCAL_RESOURCE);
×
295
    }
296

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

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

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

330
    /**
331
     * Checks if the IRI is a URI placeholder.
332
     *
333
     * @param iri the IRI to check.
334
     * @return true if the IRI is a URI placeholder, false otherwise.
335
     */
336
    public boolean isUriPlaceholder(IRI iri) {
337
        iri = transform(iri);
×
338
        if (!typeMap.containsKey(iri)) return false;
×
339
        for (IRI t : typeMap.get(iri)) {
×
340
            if (t.equals(NTEMPLATE.URI_PLACEHOLDER)) return true;
×
341
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
×
342
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
×
343
            if (t.equals(NTEMPLATE.AUTO_ESCAPE_URI_PLACEHOLDER)) return true;
×
344
            if (t.equals(NTEMPLATE.RESTRICTED_CHOICE_PLACEHOLDER)) return true;
×
345
            if (t.equals(NTEMPLATE.GUIDED_CHOICE_PLACEHOLDER)) return true;
×
346
            if (t.equals(NTEMPLATE.AGENT_PLACEHOLDER)) return true;
×
347
        }
×
348
        return false;
×
349
    }
350

351
    /**
352
     * Checks if the IRI is an external URI placeholder.
353
     *
354
     * @param iri the IRI to check.
355
     * @return true if the IRI is an external URI placeholder, false otherwise.
356
     */
357
    public boolean isExternalUriPlaceholder(IRI iri) {
358
        iri = transform(iri);
×
359
        if (!typeMap.containsKey(iri)) return false;
×
360
        for (IRI t : typeMap.get(iri)) {
×
361
            if (t.equals(NTEMPLATE.EXTERNAL_URI_PLACEHOLDER)) return true;
×
362
            if (t.equals(NTEMPLATE.TRUSTY_URI_PLACEHOLDER)) return true;
×
363
        }
×
364
        return false;
×
365
    }
366

367
    /**
368
     * Checks if the IRI is a trusty URI placeholder.
369
     *
370
     * @param iri the IRI to check.
371
     * @return true if the IRI is a trusty URI placeholder, false otherwise.
372
     */
373
    public boolean isTrustyUriPlaceholder(IRI iri) {
374
        iri = transform(iri);
×
375
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.TRUSTY_URI_PLACEHOLDER);
×
376
    }
377

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

389
    /**
390
     * Checks if the IRI is a literal placeholder.
391
     *
392
     * @param iri the IRI to check.
393
     * @return true if the IRI is a literal placeholder, false otherwise.
394
     */
395
    public boolean isLiteralPlaceholder(IRI iri) {
396
        iri = transform(iri);
×
397
        return typeMap.containsKey(iri) && (typeMap.get(iri).contains(NTEMPLATE.LITERAL_PLACEHOLDER) || typeMap.get(iri).contains(NTEMPLATE.LONG_LITERAL_PLACEHOLDER));
×
398
    }
399

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

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

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

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

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

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

480
    /**
481
     * Checks if the IRI is an optional statement.
482
     *
483
     * @param iri the IRI to check.
484
     * @return true if the IRI is an optional statement, false otherwise.
485
     */
486
    public boolean isOptionalStatement(IRI iri) {
487
        return typeMap.containsKey(iri) && typeMap.get(iri).contains(NTEMPLATE.OPTIONAL_STATEMENT);
×
488
    }
489

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

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

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

542
    /**
543
     * Returns the IRI of the default provenance for the template.
544
     *
545
     * @return the IRI of the default provenance, or null if not set.
546
     */
547
    public IRI getDefaultProvenance() {
548
        return defaultProvenance;
×
549
    }
550

551
    /**
552
     * Returns the target namespace for the template.
553
     *
554
     * @return the target namespace as a string, or null if not set.
555
     */
556
    public String getTargetNamespace() {
557
        return targetNamespace;
×
558
    }
559

560
    /**
561
     * Returns the nanopub label pattern.
562
     *
563
     * @return the nanopub label pattern as a string, or null if not set.
564
     */
565
    public String getNanopubLabelPattern() {
566
        return nanopubLabelPattern;
3✔
567
    }
568

569
    /**
570
     * Returns the list of target nanopub types.
571
     *
572
     * @return a list of IRI objects representing the target nanopub types.
573
     */
574
    public List<IRI> getTargetNanopubTypes() {
575
        return targetNanopubTypes;
×
576
    }
577

578
    /**
579
     * Returns the list of the required pubinfo elements for the template.
580
     *
581
     * @return a list of IRI objects representing the required pubinfo elements.
582
     */
583
    public List<IRI> getRequiredPubinfoElements() {
584
        return requiredPubinfoElements;
×
585
    }
586

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

607
    /**
608
     * Returns the tag associated with the template.
609
     *
610
     * @return the tag as a string, or null if not set.
611
     */
612
    public String getTag() {
613
        return tag;
3✔
614
    }
615

616
    private void processTemplate(Nanopub templateNp) throws MalformedTemplateException {
617
        boolean isNpTemplate = false;
2✔
618
        for (Statement st : templateNp.getAssertion()) {
11!
619
            if (st.getSubject().equals(templateNp.getAssertionUri()) && st.getPredicate().equals(RDF.TYPE)) {
11!
620
                if (st.getObject().equals(NTEMPLATE.ASSERTION_TEMPLATE) || st.getObject().equals(NTEMPLATE.PROVENANCE_TEMPLATE) || st.getObject().equals(NTEMPLATE.PUBINFO_TEMPLATE)) {
5!
621
                    isNpTemplate = true;
2✔
622
                    break;
1✔
623
                }
624
            }
625
        }
1✔
626

627
        if (isNpTemplate) {
2!
628
            processNpTemplate(templateNp);
4✔
629
        } else {
630
            // Experimental SHACL-based template:
631
            processShaclTemplate(templateNp);
×
632
        }
633
    }
1✔
634

635
    private void processNpTemplate(Nanopub templateNp) {
636
        templateIri = templateNp.getAssertionUri();
4✔
637
        for (Statement st : templateNp.getAssertion()) {
11✔
638
            final IRI subj = (IRI) st.getSubject();
4✔
639
            final IRI pred = st.getPredicate();
3✔
640
            final Value obj = st.getObject();
3✔
641
            final String objS = obj.stringValue();
3✔
642

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

744
    private void processShaclTemplate(Nanopub templateNp) throws MalformedTemplateException {
745
        templateIri = null;
×
746
        for (Statement st : templateNp.getAssertion()) {
×
747
            if (st.getPredicate().equals(SHACL.TARGET_CLASS)) {
×
748
                templateIri = (IRI) st.getSubject();
×
749
                break;
×
750
            }
751
        }
×
752
        if (templateIri == null) {
×
753
            throw new MalformedTemplateException("Base node shape not found");
×
754
        }
755

756
        IRI baseSubj = vf.createIRI(templateIri.stringValue() + "+subj");
×
757
        addType(baseSubj, NTEMPLATE.INTRODUCED_RESOURCE);
×
758

759
        List<IRI> statementList = new ArrayList<>();
×
760
        Map<IRI, Integer> minCounts = new HashMap<>();
×
761
        Map<IRI, Integer> maxCounts = new HashMap<>();
×
762

763
        for (Statement st : templateNp.getAssertion()) {
×
764
            final IRI subj = (IRI) st.getSubject();
×
765
            final IRI pred = st.getPredicate();
×
766
            final Value obj = st.getObject();
×
767
            final String objS = obj.stringValue();
×
768

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

913
        if (label == null) {
×
914
            label = NanopubUtils.getLabel(templateNp);
×
915
        }
916
    }
×
917

918
    private void addType(IRI thing, IRI type) {
919
        List<IRI> l = typeMap.get(thing);
6✔
920
        if (l == null) {
2✔
921
            l = new ArrayList<>();
4✔
922
            typeMap.put(thing, l);
6✔
923
        }
924
        l.add(type);
4✔
925
    }
1✔
926

927
    private void addStatement(IRI thing, IRI type) {
928
        List<IRI> l = typeMap.get(thing);
×
929
        if (l == null) {
×
930
            l = new ArrayList<>();
×
931
            typeMap.put(thing, l);
×
932
        }
933
        l.add(type);
×
934
    }
×
935

936
    private IRI transform(IRI iri) {
937
        if (iri.stringValue().matches(".*__[0-9]+")) {
×
938
            // TODO: Check that this double-underscore pattern isn't used otherwise:
939
            return vf.createIRI(iri.stringValue().replaceFirst("__[0-9]+$", ""));
×
940
        }
941
        return iri;
×
942
    }
943

944

945
    private StatementComparator statementComparator = new StatementComparator();
6✔
946

947
    private class StatementComparator implements Comparator<IRI>, Serializable {
6✔
948

949
        private static final long serialVersionUID = 1L;
950

951
        /**
952
         * Compares two IRIs based on their order in the template.
953
         *
954
         * @param arg0 the first object to be compared.
955
         * @param arg1 the second object to be compared.
956
         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
957
         */
958
        @Override
959
        public int compare(IRI arg0, IRI arg1) {
960
            Integer i0 = statementOrder.get(arg0);
7✔
961
            Integer i1 = statementOrder.get(arg1);
7✔
962
            if (i0 == null && i1 == null) return arg0.stringValue().compareTo(arg1.stringValue());
10!
963
            if (i0 == null) return 1;
×
964
            if (i1 == null) return -1;
×
965
            return i0 - i1;
×
966
        }
967

968
    }
969

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