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

knowledgepixels / nanodash / 18653097879

20 Oct 2025 01:11PM UTC coverage: 13.053% (-0.5%) from 13.583%
18653097879

push

github

web-flow
Merge pull request #265 from knowledgepixels/252-datetime-picker-datetime-literal-placeholders

Add date pickers for date/datetime literal placeholders

450 of 4316 branches covered (10.43%)

Branch coverage included in aggregate %.

1157 of 7995 relevant lines covered (14.47%)

0.65 hits per line

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

0.27
src/main/java/com/knowledgepixels/nanodash/template/TemplateContext.java
1
package com.knowledgepixels.nanodash.template;
2

3
import com.knowledgepixels.nanodash.NanodashSession;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.component.LiteralDateItem;
6
import com.knowledgepixels.nanodash.component.PublishForm.FillMode;
7
import com.knowledgepixels.nanodash.component.StatementItem;
8
import org.apache.wicket.Component;
9
import org.apache.wicket.model.IModel;
10
import org.apache.wicket.model.Model;
11
import org.eclipse.rdf4j.model.IRI;
12
import org.eclipse.rdf4j.model.Statement;
13
import org.eclipse.rdf4j.model.Value;
14
import org.eclipse.rdf4j.model.ValueFactory;
15
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
16
import org.eclipse.rdf4j.model.vocabulary.RDFS;
17
import org.eclipse.rdf4j.model.vocabulary.XSD;
18
import org.nanopub.*;
19
import org.nanopub.vocabulary.NTEMPLATE;
20

21
import java.io.Serializable;
22
import java.time.ZonedDateTime;
23
import java.util.*;
24

25
/**
26
 * Context for a template, containing all necessary information to fill.
27
 */
28
public class TemplateContext implements Serializable {
29

30
    private static final ValueFactory vf = SimpleValueFactory.getInstance();
3✔
31

32
    private final ContextType contextType;
33
    private final Template template;
34
    private final String componentId;
35
    private final Map<String, String> params = new HashMap<>();
×
36
    private List<Component> components = new ArrayList<>();
×
37
    private final Map<IRI, IModel<?>> componentModels = new HashMap<>();
×
38
    private Set<IRI> introducedIris = new HashSet<>();
×
39
    private Set<IRI> embeddedIris = new HashSet<>();
×
40
    private List<StatementItem> statementItems;
41
    private Set<IRI> iriSet = new HashSet<>();
×
42
    private Map<IRI, StatementItem> narrowScopeMap = new HashMap<>();
×
43
    private String targetNamespace = Template.DEFAULT_TARGET_NAMESPACE;
×
44
    private Nanopub existingNanopub;
45
    private Map<IRI, String> labels;
46
    private FillMode fillMode = null;
×
47

48
    /**
49
     * Constructor for creating a new template context for filling a template.
50
     *
51
     * @param contextType     the type of context
52
     * @param templateId      the ID of the template to fill
53
     * @param componentId     the ID of the component that will use this context
54
     * @param targetNamespace the target namespace for the template, can be null to use the default namespace
55
     */
56
    public TemplateContext(ContextType contextType, String templateId, String componentId, String targetNamespace) {
57
        this(contextType, templateId, componentId, targetNamespace, null);
×
58
    }
×
59

60
    /**
61
     * Constructor for creating a new template context for filling a template.
62
     *
63
     * @param contextType     the type of context
64
     * @param templateId      the ID of the template to fill
65
     * @param componentId     the ID of the component that will use this context
66
     * @param existingNanopub an existing nanopub to fill, can be null if creating a new nanopub
67
     */
68
    public TemplateContext(ContextType contextType, String templateId, String componentId, Nanopub existingNanopub) {
69
        this(contextType, templateId, componentId, null, existingNanopub);
×
70
    }
×
71

72
    private TemplateContext(ContextType contextType, String templateId, String componentId, String targetNamespace, Nanopub existingNanopub) {
×
73
        this.contextType = contextType;
×
74
        // TODO: check whether template is of correct type:
75
        this.template = TemplateData.get().getTemplate(templateId);
×
76
        this.componentId = componentId;
×
77
        if (targetNamespace != null) {
×
78
            this.targetNamespace = targetNamespace;
×
79
        }
80
        this.existingNanopub = existingNanopub;
×
81
        if (existingNanopub == null && NanodashSession.get().getUserIri() != null) {
×
82
            componentModels.put(NTEMPLATE.CREATOR_PLACEHOLDER, Model.of(NanodashSession.get().getUserIri().stringValue()));
×
83
        }
84
    }
×
85

86
    /**
87
     * Initializes the statements for this context.
88
     */
89
    public void initStatements() {
90
        if (statementItems != null) return;
×
91
        statementItems = new ArrayList<>();
×
92
        for (IRI st : template.getStatementIris()) {
×
93
            StatementItem<?> si = new StatementItem<>(componentId, st, this);
×
94
            statementItems.add(si);
×
95
            for (IRI i : si.getIriSet()) {
×
96
                if (iriSet.contains(i)) {
×
97
                    narrowScopeMap.remove(i);
×
98
                } else {
99
                    iriSet.add(i);
×
100
                    narrowScopeMap.put(i, si);
×
101
                }
102
            }
×
103
        }
×
104
    }
×
105

106
    /**
107
     * Finalizes the statements by processing all parameters and setting the repetition counts.
108
     */
109
    public void finalizeStatements() {
110
        Map<StatementItem<?>, Integer> finalRepetitionCount = new HashMap<>();
×
111
        for (IRI ni : narrowScopeMap.keySet()) {
×
112
            // TODO: Move all occurrences of this to utility function:
113
            String postfix = Utils.getUriPostfix(ni);
×
114
            StatementItem si = narrowScopeMap.get(ni);
×
115
            int i = si.getRepetitionCount();
×
116
            while (true) {
117
                String p = postfix + "__" + i;
×
118
                if (hasParam(p)) {
×
119
                    si.addRepetitionGroup();
×
120
                } else {
121
                    break;
122
                }
123
                i++;
×
124
            }
×
125
            i = 1;
×
126
            int corr = 0;
×
127
            if (si.isEmpty()) corr = 1;
×
128
            while (true) {
129
                String p = postfix + "__." + i;
×
130
                if (hasParam(p)) {
×
131
                    int absPos = si.getRepetitionCount() + i - 1 - corr;
×
132
                    String param = postfix + "__" + absPos;
×
133
                    if (i - corr == 0) param = postfix;
×
134
                    setParam(param, getParam(p));
×
135
                    finalRepetitionCount.put(si, i - corr);
×
136
                } else {
137
                    break;
138
                }
139
                i++;
×
140
            }
×
141
        }
×
142
        for (StatementItem si : finalRepetitionCount.keySet()) {
×
143
            for (int i = 0; i < finalRepetitionCount.get(si); i++) {
×
144
                si.addRepetitionGroup();
×
145
            }
146
        }
×
147
        for (StatementItem si : statementItems) {
×
148
            si.finalizeValues();
×
149
        }
×
150
    }
×
151

152
    /**
153
     * Sets the fill mode for this context.
154
     *
155
     * @param fillMode the fill mode to set
156
     */
157
    public void setFillMode(FillMode fillMode) {
158
        this.fillMode = fillMode;
×
159
    }
×
160

161
    /**
162
     * Gets the fill mode for this context.
163
     *
164
     * @return the fill mode, or null if not set
165
     */
166
    public FillMode getFillMode() {
167
        return fillMode;
×
168
    }
169

170
    /**
171
     * Returns the type of context.
172
     *
173
     * @return the context type
174
     */
175
    public ContextType getType() {
176
        return contextType;
×
177
    }
178

179
    /**
180
     * Returns the template associated with this context.
181
     *
182
     * @return the template
183
     */
184
    public Template getTemplate() {
185
        return template;
×
186
    }
187

188
    /**
189
     * Returns the ID of the template associated with this context.
190
     *
191
     * @return the template ID
192
     */
193
    public String getTemplateId() {
194
        return template.getId();
×
195
    }
196

197
    /**
198
     * Sets a parameter for this context.
199
     *
200
     * @param name  the name of the parameter
201
     * @param value the value of the parameter
202
     */
203
    public void setParam(String name, String value) {
204
        params.put(name, value);
×
205
    }
×
206

207
    /**
208
     * Gets a parameter value by its name.
209
     *
210
     * @param name the name of the parameter
211
     * @return the value of the parameter, or null if not set
212
     */
213
    public String getParam(String name) {
214
        return params.get(name);
×
215
    }
216

217
    /**
218
     * Checks if a parameter with the given name exists.
219
     *
220
     * @param name the name of the parameter
221
     * @return true if the parameter exists, false otherwise
222
     */
223
    public boolean hasParam(String name) {
224
        return params.containsKey(name);
×
225
    }
226

227
    /**
228
     * Returns the components associated with this context.
229
     *
230
     * @return a list of components
231
     */
232
    public List<Component> getComponents() {
233
        return components;
×
234
    }
235

236
    /**
237
     * Returns the component models associated with this context.
238
     *
239
     * @return a map of IRI to model of strings
240
     */
241
    public Map<IRI, IModel<?>> getComponentModels() {
242
        return componentModels;
×
243
    }
244

245
    /**
246
     * Returns the introduced IRIs in this context.
247
     *
248
     * @return a set of introduced IRIs
249
     */
250
    public Set<IRI> getIntroducedIris() {
251
        return introducedIris;
×
252
    }
253

254
    /**
255
     * Returns the embedded IRIs in this context.
256
     *
257
     * @return a set of embedded IRIs
258
     */
259
    public Set<IRI> getEmbeddedIris() {
260
        return embeddedIris;
×
261
    }
262

263
    /**
264
     * Processes an IRI by applying the template's processing rules.
265
     *
266
     * @param iri the IRI to process
267
     * @return the processed IRI, or null if the processing results in no value
268
     */
269
    public IRI processIri(IRI iri) {
270
        Value v = processValue(iri);
×
271
        if (v == null) return null;
×
272
        if (v instanceof IRI) return (IRI) v;
×
273
        return iri;
×
274
    }
275

276
    /**
277
     * Processes a Value according to the template's rules.
278
     *
279
     * @param value the Value to process
280
     * @return the processed Value, or the original Value if no processing is applicable
281
     */
282
    public Value processValue(Value value) {
283
        if (!(value instanceof IRI)) {
×
284
            return value;
×
285
        }
286
        IRI iri = (IRI) value;
×
287
        if (iri.equals(NTEMPLATE.CREATOR_PLACEHOLDER)) {
×
288
            iri = NanodashSession.get().getUserIri();
×
289
        }
290
        if (iri.equals(NTEMPLATE.ASSERTION_PLACEHOLDER)) {
×
291
            iri = vf.createIRI(targetNamespace + "assertion");
×
292
        } else if (iri.equals(NTEMPLATE.NANOPUB_PLACEHOLDER)) {
×
293
            iri = vf.createIRI(targetNamespace);
×
294
        }
295
        // TODO: Move this code below to the respective placeholder classes:
296
        IModel<?> tf = componentModels.get(iri);
×
297
        Value processedValue = null;
×
298
        Object tfObjectGeneric = null;
×
299
        if (tf != null) {
×
300
            tfObjectGeneric = tf.getObject();
×
301
        }
302
        if (template.isRestrictedChoicePlaceholder(iri)) {
×
303
            String tfObject = (String) tfObjectGeneric;
×
304
            if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
305
                String prefix = template.getPrefix(iri);
×
306
                if (prefix == null) prefix = "";
×
307
                if (template.isLocalResource(iri)) prefix = targetNamespace;
×
308
                if (tfObject.matches("https?://.+")) prefix = "";
×
309
                String v = prefix + tf.getObject();
×
310
                if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
311
                if (v.matches("https?://.*")) {
×
312
                    processedValue = vf.createIRI(v);
×
313
                } else {
314
                    processedValue = vf.createLiteral(tfObject);
×
315
                }
316
            }
317
        } else if (template.isUriPlaceholder(iri)) {
×
318
            String tfObject = (String) tfObjectGeneric;
×
319
            if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
320
                String prefix = template.getPrefix(iri);
×
321
                if (prefix == null) prefix = "";
×
322
                if (template.isLocalResource(iri)) prefix = targetNamespace;
×
323
                String v;
324
                if (template.isAutoEscapePlaceholder(iri)) {
×
325
                    v = prefix + Utils.urlEncode(tf.getObject());
×
326
                } else {
327
                    if (tfObject.matches("https?://.+")) prefix = "";
×
328
                    v = prefix + tf.getObject();
×
329
                }
330
                if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
331
                processedValue = vf.createIRI(v);
×
332
            }
333
        } else if (template.isLocalResource(iri)) {
×
334
            String tfObject = (String) tfObjectGeneric;
×
335
            if (template.isIntroducedResource(iri) && fillMode == FillMode.SUPERSEDE) {
×
336
                if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
337
                    processedValue = vf.createIRI(tfObject);
×
338
                }
339
            } else {
340
                String prefix = Utils.getUriPrefix(iri);
×
341
                processedValue = vf.createIRI(iri.stringValue().replace(prefix, targetNamespace));
×
342
            }
343
        } else if (template.isLiteralPlaceholder(iri)) {
×
344
            IRI datatype = template.getDatatype(iri);
×
345
            String languageTag = template.getLanguageTag(iri);
×
346
            if (datatype != null) {
×
347
                if (datatype.equals(XSD.DATETIME)) {
×
348
                    ZonedDateTime tfObject = (ZonedDateTime) tfObjectGeneric;
×
349
                    if (tfObject != null) {
×
350
                        processedValue = vf.createLiteral(tfObject);
×
351
                    }
352
                } else if (datatype.equals(XSD.DATE)) {
×
353
                    Date tfObject = (Date) tfObjectGeneric;
×
354
                    if (tfObject != null) {
×
355
                        processedValue = vf.createLiteral(LiteralDateItem.format.format(tfObject), datatype);
×
356
                    }
357
                }
×
358
            } else {
359
                String tfObject = (String) tfObjectGeneric;
×
360
                if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
361
                    if (datatype != null) {
×
362
                        processedValue = vf.createLiteral(tfObject, datatype);
×
363
                    } else if (languageTag != null) {
×
364
                        processedValue = vf.createLiteral(tfObject, languageTag);
×
365
                    } else {
366
                        processedValue = vf.createLiteral(tfObject);
×
367
                    }
368
                }
369
            }
370
        } else if (template.isValuePlaceholder(iri)) {
×
371
            String tfObject = (String) tfObjectGeneric;
×
372
            if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
373
                if (Utils.isValidLiteralSerialization(tfObject)) {
×
374
                    processedValue = Utils.getParsedLiteral(tfObject);
×
375
                } else {
376
                    String v = tfObject;
×
377
                    if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
378
                    processedValue = vf.createIRI(v);
×
379
                }
380
            }
381
        } else if (template.isSequenceElementPlaceholder(iri)) {
×
382
            String tfObject = (String) tfObjectGeneric;
×
383
            if (tf != null && tfObject != null && !tfObject.isEmpty()) {
×
384
                processedValue = vf.createIRI(tfObject);
×
385
            }
386
        } else {
×
387
            processedValue = iri;
×
388
        }
389
        if (processedValue instanceof IRI pvIri && template.isIntroducedResource(iri)) {
×
390
            introducedIris.add(pvIri);
×
391
        }
392
        if (processedValue instanceof IRI pvIri && template.isEmbeddedResource(iri)) {
×
393
            embeddedIris.add(pvIri);
×
394
        }
395
        return processedValue;
×
396
    }
397

398
    /**
399
     * Returns the statement items associated with this context.
400
     *
401
     * @return a list of StatementItem objects
402
     */
403
    public List<StatementItem> getStatementItems() {
404
        return statementItems;
×
405
    }
406

407
    /**
408
     * Propagates the statements from this context to a NanopubCreator.
409
     *
410
     * @param npCreator the NanopubCreator to which the statements will be added
411
     * @throws org.nanopub.MalformedNanopubException        if there is an error in the nanopub structure
412
     * @throws org.nanopub.NanopubAlreadyFinalizedException if the nanopub has already been finalized
413
     */
414
    public void propagateStatements(NanopubCreator npCreator) throws MalformedNanopubException, NanopubAlreadyFinalizedException {
415
        if (template.getNanopub() instanceof NanopubWithNs) {
×
416
            NanopubWithNs np = (NanopubWithNs) template.getNanopub();
×
417
            for (String p : np.getNsPrefixes()) {
×
418
                npCreator.addNamespace(p, np.getNamespace(p));
×
419
            }
×
420
        }
421
        for (StatementItem si : statementItems) {
×
422
            si.addTriplesTo(npCreator);
×
423
        }
×
424
    }
×
425

426
    /**
427
     * Checks if the context has a narrow scope for the given IRI.
428
     *
429
     * @param iri the IRI to check
430
     * @return true if there is a narrow scope for the IRI, false otherwise
431
     */
432
    public boolean hasNarrowScope(IRI iri) {
433
        return narrowScopeMap.containsKey(iri);
×
434
    }
435

436
    /**
437
     * Checks if any of the statement items in this context will match any triple.
438
     *
439
     * @return true if any statement item will match any triple, false otherwise
440
     */
441
    public boolean willMatchAnyTriple() {
442
        initStatements();
×
443
        for (StatementItem si : statementItems) {
×
444
            if (si.willMatchAnyTriple()) return true;
×
445
        }
×
446
        return false;
×
447
    }
448

449
    /**
450
     * Fills the context with statements, processing each StatementItem.
451
     *
452
     * @param statements the list of statements to fill
453
     * @throws com.knowledgepixels.nanodash.template.UnificationException if there is an error during unification of statements
454
     */
455
    public void fill(List<Statement> statements) throws UnificationException {
456
        for (StatementItem si : statementItems) {
×
457
            si.fill(statements);
×
458
        }
×
459
        for (StatementItem si : statementItems) {
×
460
            si.fillFinished();
×
461
        }
×
462
    }
×
463

464
    /**
465
     * Returns the existing Nanopub associated with this context, if any.
466
     *
467
     * @return the existing Nanopub, or null if this context is for a new Nanopub
468
     */
469
    public Nanopub getExistingNanopub() {
470
        return existingNanopub;
×
471
    }
472

473
    /**
474
     * Checks if this context is read-only.
475
     *
476
     * @return true if the context is read-only, false otherwise
477
     */
478
    public boolean isReadOnly() {
479
        return existingNanopub != null;
×
480
    }
481

482
    /**
483
     * Returns the label for a given IRI, if available.
484
     *
485
     * @param iri the IRI for which to get the label
486
     * @return the label as a String, or null if no label is found
487
     */
488
    public String getLabel(IRI iri) {
489
        if (existingNanopub == null) return null;
×
490
        if (labels == null) {
×
491
            labels = new HashMap<>();
×
492
            for (Statement st : existingNanopub.getPubinfo()) {
×
493
                if (st.getPredicate().equals(NTEMPLATE.HAS_LABEL_FROM_API) || st.getPredicate().equals(RDFS.LABEL)) {
×
494
                    String label = st.getObject().stringValue();
×
495
                    labels.put((IRI) st.getSubject(), label);
×
496
                }
497
            }
×
498
        }
499
        return labels.get(iri);
×
500
    }
501

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