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

knowledgepixels / nanodash / 17427371198

03 Sep 2025 08:16AM UTC coverage: 12.119% (+0.06%) from 12.064%
17427371198

push

github

tkuhn
Improve refreshUsers()

335 of 3870 branches covered (8.66%)

Branch coverage included in aggregate %.

972 of 6915 relevant lines covered (14.06%)

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 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 final long serialVersionUID = 1L;
39

40
    private static ValueFactory vf = SimpleValueFactory.getInstance();
2✔
41
    private static final Logger logger = LoggerFactory.getLogger(Template.class);
4✔
42

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

637
        if (isNpTemplate) {
2!
638
            processNpTemplate(templateNp);
4✔
639
        } else {
640
            // Experimental SHACL-based template:
641
            processShaclTemplate(templateNp);
×
642
        }
643
    }
1✔
644

645
    private void processNpTemplate(Nanopub templateNp) {
646
        templateIri = templateNp.getAssertionUri();
4✔
647
        for (Statement st : templateNp.getAssertion()) {
11✔
648
            final IRI subj = (IRI) st.getSubject();
4✔
649
            final IRI pred = st.getPredicate();
3✔
650
            final Value obj = st.getObject();
3✔
651
            final String objS = obj.stringValue();
3✔
652

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

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

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

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

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

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

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

928
    private void addType(IRI thing, IRI type) {
929
        List<IRI> l = typeMap.get(thing);
6✔
930
        if (l == null) {
2✔
931
            l = new ArrayList<>();
4✔
932
            typeMap.put(thing, l);
6✔
933
        }
934
        l.add(type);
4✔
935
    }
1✔
936

937
    private void addStatement(IRI thing, IRI type) {
938
        List<IRI> l = typeMap.get(thing);
×
939
        if (l == null) {
×
940
            l = new ArrayList<>();
×
941
            typeMap.put(thing, l);
×
942
        }
943
        l.add(type);
×
944
    }
×
945

946
    private IRI transform(IRI iri) {
947
        if (iri.stringValue().matches(".*__[0-9]+")) {
×
948
            // TODO: Check that this double-underscore pattern isn't used otherwise:
949
            return vf.createIRI(iri.stringValue().replaceFirst("__[0-9]+$", ""));
×
950
        }
951
        return iri;
×
952
    }
953

954

955
    private StatementComparator statementComparator = new StatementComparator();
6✔
956

957
    private class StatementComparator implements Comparator<IRI>, Serializable {
6✔
958

959
        private static final long serialVersionUID = 1L;
960

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

978
    }
979

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