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

knowledgepixels / nanodash / 17235948541

26 Aug 2025 10:54AM UTC coverage: 12.455% (-0.02%) from 12.474%
17235948541

push

github

tkuhn
Use Literals.normalizeLanguageTag for normalizing literal language tags

332 of 3800 branches covered (8.74%)

Branch coverage included in aggregate %.

988 of 6798 relevant lines covered (14.53%)

0.64 hits per line

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

0.0
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.PublishForm.FillMode;
6
import com.knowledgepixels.nanodash.component.StatementItem;
7
import org.apache.wicket.Component;
8
import org.apache.wicket.model.IModel;
9
import org.apache.wicket.model.Model;
10
import org.eclipse.rdf4j.model.IRI;
11
import org.eclipse.rdf4j.model.Statement;
12
import org.eclipse.rdf4j.model.Value;
13
import org.eclipse.rdf4j.model.ValueFactory;
14
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
15
import org.eclipse.rdf4j.model.vocabulary.RDFS;
16
import org.nanopub.MalformedNanopubException;
17
import org.nanopub.Nanopub;
18
import org.nanopub.NanopubCreator;
19
import org.nanopub.NanopubWithNs;
20

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

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

29
    private static final long serialVersionUID = 1L;
30

31
    private static ValueFactory vf = SimpleValueFactory.getInstance();
×
32

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

277
    /**
278
     * Processes a Value according to the template's rules.
279
     *
280
     * @param value the Value to process
281
     * @return the processed Value, or the original Value if no processing is applicable
282
     */
283
    public Value processValue(Value value) {
284
        if (!(value instanceof IRI)) return value;
×
285
        IRI iri = (IRI) value;
×
286
        if (iri.equals(Template.CREATOR_PLACEHOLDER)) {
×
287
            iri = NanodashSession.get().getUserIri();
×
288
        }
289
        if (iri.equals(Template.ASSERTION_PLACEHOLDER)) {
×
290
            iri = vf.createIRI(targetNamespace + "assertion");
×
291
        } else if (iri.equals(Template.NANOPUB_PLACEHOLDER)) {
×
292
            iri = vf.createIRI(targetNamespace);
×
293
        }
294
        // TODO: Move this code below to the respective placeholder classes:
295
        IModel<String> tf = componentModels.get(iri);
×
296
        Value processedValue = null;
×
297
        if (template.isRestrictedChoicePlaceholder(iri)) {
×
298
            if (tf != null && tf.getObject() != null && !tf.getObject().isEmpty()) {
×
299
                String prefix = template.getPrefix(iri);
×
300
                if (prefix == null) prefix = "";
×
301
                if (template.isLocalResource(iri)) prefix = targetNamespace;
×
302
                if (tf.getObject().matches("https?://.+")) prefix = "";
×
303
                String v = prefix + tf.getObject();
×
304
                if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
305
                if (v.matches("https?://.*")) {
×
306
                    processedValue = vf.createIRI(v);
×
307
                } else {
308
                    processedValue = vf.createLiteral(tf.getObject());
×
309
                }
310
            }
×
311
        } else if (template.isUriPlaceholder(iri)) {
×
312
            if (tf != null && tf.getObject() != null && !tf.getObject().isEmpty()) {
×
313
                String prefix = template.getPrefix(iri);
×
314
                if (prefix == null) prefix = "";
×
315
                if (template.isLocalResource(iri)) prefix = targetNamespace;
×
316
                String v;
317
                if (template.isAutoEscapePlaceholder(iri)) {
×
318
                    v = prefix + Utils.urlEncode(tf.getObject());
×
319
                } else {
320
                    if (tf.getObject().matches("https?://.+")) prefix = "";
×
321
                    v = prefix + tf.getObject();
×
322
                }
323
                if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
324
                processedValue = vf.createIRI(v);
×
325
            }
×
326
        } else if (template.isLocalResource(iri)) {
×
327
            String prefix = Utils.getUriPrefix(iri);
×
328
            processedValue = vf.createIRI(iri.stringValue().replace(prefix, targetNamespace));
×
329
        } else if (template.isLiteralPlaceholder(iri)) {
×
330
            IRI datatype = template.getDatatype(iri);
×
331
            String language = template.getLanguageAttribute(iri);
×
332
            if (tf != null && tf.getObject() != null && !tf.getObject().isEmpty()) {
×
333
                if (datatype != null) {
×
334
                    processedValue = vf.createLiteral(tf.getObject(), datatype);
×
335
                } else if (language != null) {
×
336
                    processedValue = vf.createLiteral(tf.getObject(), language);
×
337
                } else {
338
                    processedValue = vf.createLiteral(tf.getObject());
×
339
                }
340
            }
341
        
342
        } else if (template.isValuePlaceholder(iri)) {
×
343
            if (tf != null && tf.getObject() != null && !tf.getObject().isEmpty()) {
×
344
                if (tf.getObject().startsWith("\"") && tf.getObject().endsWith("\"")) {
×
345
                    processedValue = vf.createLiteral(tf.getObject().substring(1, tf.getObject().length() - 1).replaceAll("\\\\(\\\\|\\\")", "$1"));
×
346
                } else {
347
                    String v = tf.getObject();
×
348
                    if (v.matches("[^:# ]+")) v = targetNamespace + v;
×
349
                    processedValue = vf.createIRI(v);
×
350
                }
×
351
            }
352
        } else if (template.isSequenceElementPlaceholder(iri)) {
×
353
            if (tf != null && tf.getObject() != null && !tf.getObject().isEmpty()) {
×
354
                processedValue = vf.createIRI(tf.getObject());
×
355
            }
356
        } else {
357
            processedValue = iri;
×
358
        }
359
        if (processedValue instanceof IRI pvIri && template.isIntroducedResource(iri)) {
×
360
            introducedIris.add(pvIri);
×
361
        }
362
        if (processedValue instanceof IRI pvIri && template.isEmbeddedResource(iri)) {
×
363
            embeddedIris.add(pvIri);
×
364
        }
365
        return processedValue;
×
366
    }
367

368
    /**
369
     * Returns the statement items associated with this context.
370
     *
371
     * @return a list of StatementItem objects
372
     */
373
    public List<StatementItem> getStatementItems() {
374
        return statementItems;
×
375
    }
376

377
    /**
378
     * Propagates the statements from this context to a NanopubCreator.
379
     *
380
     * @param npCreator the NanopubCreator to which the statements will be added
381
     * @throws org.nanopub.MalformedNanopubException if there is an error in the nanopub structure
382
     */
383
    public void propagateStatements(NanopubCreator npCreator) throws MalformedNanopubException {
384
        if (template.getNanopub() instanceof NanopubWithNs) {
×
385
            NanopubWithNs np = (NanopubWithNs) template.getNanopub();
×
386
            for (String p : np.getNsPrefixes()) {
×
387
                npCreator.addNamespace(p, np.getNamespace(p));
×
388
            }
×
389
        }
390
        for (StatementItem si : statementItems) {
×
391
            si.addTriplesTo(npCreator);
×
392
        }
×
393
    }
×
394

395
    /**
396
     * Checks if the context has a narrow scope for the given IRI.
397
     *
398
     * @param iri the IRI to check
399
     * @return true if there is a narrow scope for the IRI, false otherwise
400
     */
401
    public boolean hasNarrowScope(IRI iri) {
402
        return narrowScopeMap.containsKey(iri);
×
403
    }
404

405
    /**
406
     * Checks if any of the statement items in this context will match any triple.
407
     *
408
     * @return true if any statement item will match any triple, false otherwise
409
     */
410
    public boolean willMatchAnyTriple() {
411
        initStatements();
×
412
        for (StatementItem si : statementItems) {
×
413
            if (si.willMatchAnyTriple()) return true;
×
414
        }
×
415
        return false;
×
416
    }
417

418
    /**
419
     * Fills the context with statements, processing each StatementItem.
420
     *
421
     * @param statements the list of statements to fill
422
     * @throws com.knowledgepixels.nanodash.template.UnificationException if there is an error during unification of statements
423
     */
424
    public void fill(List<Statement> statements) throws UnificationException {
425
        for (StatementItem si : statementItems) {
×
426
            si.fill(statements);
×
427
        }
×
428
        for (StatementItem si : statementItems) {
×
429
            si.fillFinished();
×
430
        }
×
431
    }
×
432

433
    /**
434
     * Returns the existing Nanopub associated with this context, if any.
435
     *
436
     * @return the existing Nanopub, or null if this context is for a new Nanopub
437
     */
438
    public Nanopub getExistingNanopub() {
439
        return existingNanopub;
×
440
    }
441

442
    /**
443
     * Checks if this context is read-only.
444
     *
445
     * @return true if the context is read-only, false otherwise
446
     */
447
    public boolean isReadOnly() {
448
        return existingNanopub != null;
×
449
    }
450

451
    /**
452
     * Returns the label for a given IRI, if available.
453
     *
454
     * @param iri the IRI for which to get the label
455
     * @return the label as a String, or null if no label is found
456
     */
457
    public String getLabel(IRI iri) {
458
        if (existingNanopub == null) return null;
×
459
        if (labels == null) {
×
460
            labels = new HashMap<>();
×
461
            for (Statement st : existingNanopub.getPubinfo()) {
×
462
                if (st.getPredicate().equals(Template.HAS_LABEL_FROM_API) || st.getPredicate().equals(RDFS.LABEL)) {
×
463
                    String label = st.getObject().stringValue();
×
464
                    labels.put((IRI) st.getSubject(), label);
×
465
                }
466
            }
×
467
        }
468
        return labels.get(iri);
×
469
    }
470

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