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

knowledgepixels / nanodash / 24767978900

22 Apr 2026 08:19AM UTC coverage: 17.408% (-0.02%) from 17.424%
24767978900

push

github

web-flow
Merge pull request #446 from knowledgepixels/fix-placeholder-swap-after-validation-error

fix: clear rawInput when removing repetition groups

899 of 6200 branches covered (14.5%)

Branch coverage included in aggregate %.

2156 of 11349 relevant lines covered (19.0%)

2.63 hits per line

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

0.0
src/main/java/com/knowledgepixels/nanodash/component/StatementItem.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.template.ContextType;
4
import com.knowledgepixels.nanodash.template.Template;
5
import com.knowledgepixels.nanodash.template.TemplateContext;
6
import com.knowledgepixels.nanodash.template.UnificationException;
7
import org.apache.wicket.AttributeModifier;
8
import org.apache.wicket.Component;
9
import org.apache.wicket.ajax.AjaxEventBehavior;
10
import org.apache.wicket.ajax.AjaxRequestTarget;
11
import org.apache.wicket.behavior.AttributeAppender;
12
import org.apache.wicket.markup.html.WebMarkupContainer;
13
import org.apache.wicket.markup.html.basic.Label;
14
import org.apache.wicket.markup.html.form.FormComponent;
15
import org.apache.wicket.markup.html.list.ListItem;
16
import org.apache.wicket.markup.html.list.ListView;
17
import org.apache.wicket.markup.html.panel.Panel;
18
import org.apache.wicket.model.IModel;
19
import org.eclipse.rdf4j.model.IRI;
20
import org.eclipse.rdf4j.model.Statement;
21
import org.eclipse.rdf4j.model.Value;
22
import org.eclipse.rdf4j.model.ValueFactory;
23
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
24
import org.nanopub.MalformedNanopubException;
25
import org.nanopub.NanopubAlreadyFinalizedException;
26
import org.nanopub.NanopubCreator;
27
import org.nanopub.vocabulary.NTEMPLATE;
28
import org.slf4j.Logger;
29
import org.slf4j.LoggerFactory;
30

31
import java.io.Serializable;
32
import java.util.*;
33

34
/**
35
 * Represents a single item in a statement, which can be a subject, predicate, or object.
36
 */
37
public class StatementItem extends Panel {
38

39
    private TemplateContext context;
40
    private IRI statementId;
41
    private List<IRI> statementPartIds = new ArrayList<>();
×
42
    private List<WebMarkupContainer> viewElements = new ArrayList<>();
×
43
    private List<RepetitionGroup> repetitionGroups = new ArrayList<>();
×
44
    private boolean repetitionGroupsChanged = true;
×
45
    private Set<IRI> iriSet = new HashSet<>();
×
46
    private boolean isMatched = false;
×
47
    private static final Logger logger = LoggerFactory.getLogger(StatementItem.class);
×
48

49
    /**
50
     * Constructor for creating a StatementItem with a specific ID and statement ID.
51
     *
52
     * @param id          the Wicket component ID
53
     * @param statementId the IRI of the statement this item represents
54
     * @param context     the template context containing information about the template and its items
55
     */
56
    public StatementItem(String id, IRI statementId, TemplateContext context) {
57
        super(id);
×
58

59
        this.statementId = statementId;
×
60
        this.context = context;
×
61
        setOutputMarkupId(true);
×
62

63
        if (isGrouped()) {
×
64
            statementPartIds.addAll(getTemplate().getStatementIris(statementId));
×
65
        } else {
66
            statementPartIds.add(statementId);
×
67
        }
68

69
        addRepetitionGroup();
×
70

71
        ListView<WebMarkupContainer> v = new ListView<WebMarkupContainer>("statement-group", viewElements) {
×
72

73
            @Override
74
            protected void populateItem(ListItem<WebMarkupContainer> item) {
75
                item.add(item.getModelObject());
×
76
            }
×
77

78
        };
79
        v.setOutputMarkupId(true);
×
80
        add(v);
×
81
    }
×
82

83
    /**
84
     * Adds a new repetition group to this StatementItem with a default RepetitionGroup.
85
     */
86
    public void addRepetitionGroup() {
87
        addRepetitionGroup(new RepetitionGroup());
×
88
    }
×
89

90
    /**
91
     * Adds a new repetition group to this StatementItem.
92
     *
93
     * @param rg the RepetitionGroup to add
94
     */
95
    public void addRepetitionGroup(RepetitionGroup rg) {
96
        repetitionGroups.add(rg);
×
97
        repetitionGroupsChanged = true;
×
98
    }
×
99

100
    /**
101
     * {@inheritDoc}
102
     */
103
    @Override
104
    protected void onBeforeRender() {
105
        if (repetitionGroupsChanged) {
×
106
            updateViewElements();
×
107
            finalizeValues();
×
108
        }
109
        repetitionGroupsChanged = false;
×
110
        super.onBeforeRender();
×
111
    }
×
112

113
    private void updateViewElements() {
114
        viewElements.clear();
×
115
        boolean first = true;
×
116
        for (RepetitionGroup r : repetitionGroups) {
×
117
            if (isGrouped() && !first) {
×
118
                viewElements.add(new HorizontalLine("statement"));
×
119
            }
120
            viewElements.addAll(r.getStatementParts());
×
121
            boolean isOnly = repetitionGroups.size() == 1;
×
122
            boolean isLast = repetitionGroups.get(repetitionGroups.size() - 1) == r;
×
123
            r.addRepetitionButton.setVisible(!context.isReadOnly() && isRepeatable() && isLast);
×
124
            r.removeRepetitionButton.setVisible(!context.isReadOnly() && isRepeatable() && !isOnly);
×
125
            r.optionalMark.setVisible(isOnly);
×
126
            first = false;
×
127
        }
×
128
        String htmlClassString = "";
×
129
        if (!context.isReadOnly()) {
×
130
            if (isOptional()) {
×
131
                htmlClassString += "nanopub-optional ";
×
132
            }
133
            if (isAdvanced()) {
×
134
                htmlClassString += "advanced ";
×
135
            }
136
        }
137
        boolean singleItem = context.getStatementItems().size() == 1;
×
138
        boolean repeatableOrRepeated = (!context.isReadOnly() && isRepeatable()) || (context.isReadOnly() && getRepetitionCount() > 1);
×
139
        if ((isGrouped() || repeatableOrRepeated) && !singleItem) {
×
140
            htmlClassString += "nanopub-group ";
×
141
        }
142
        if (!htmlClassString.isEmpty()) {
×
143
            add(new AttributeModifier("class", htmlClassString));
×
144
        }
145
    }
×
146

147
    /**
148
     * Adds the triples of this statement item to the given NanopubCreator.
149
     *
150
     * @param npCreator the NanopubCreator to which the triples will be added
151
     * @throws org.nanopub.MalformedNanopubException        if the statement item is not properly set up
152
     * @throws org.nanopub.NanopubAlreadyFinalizedException if the NanopubCreator has already been finalized
153
     */
154
    public void addTriplesTo(NanopubCreator npCreator) throws MalformedNanopubException, NanopubAlreadyFinalizedException {
155
        if (hasEmptyElements()) {
×
156
            if (isOptional()) {
×
157
                return;
×
158
            } else {
159
                throw new MalformedNanopubException("Field of statement not set.");
×
160
            }
161
        }
162
        for (RepetitionGroup rg : repetitionGroups) {
×
163
            rg.addTriplesTo(npCreator);
×
164
        }
×
165
    }
×
166

167
    private Template getTemplate() {
168
        return context.getTemplate();
×
169
    }
170

171
    /**
172
     * Returns the number of the repetition groups for this statement item.
173
     *
174
     * @return the number of repetition groups
175
     */
176
    public int getRepetitionCount() {
177
        return repetitionGroups.size();
×
178
    }
179

180
    /**
181
     * Returns whether the statement is optional.
182
     */
183
    public boolean isOptional() {
184
        return repetitionGroups.size() == 1 && getTemplate().isOptionalStatement(statementId);
×
185
    }
186

187
    /**
188
     * Returns whether the statement is advanced.
189
     */
190
    public boolean isAdvanced() {
191
        return getTemplate().isAdvancedStatement(statementId);
×
192
    }
193

194
    /**
195
     * Checks if this statement item is grouped.
196
     *
197
     * @return true if the statement item is grouped, false otherwise
198
     */
199
    public boolean isGrouped() {
200
        return getTemplate().isGroupedStatement(statementId);
×
201
    }
202

203
    /**
204
     * Checks if this statement item is repeatable.
205
     *
206
     * @return true if the statement item is repeatable, false otherwise
207
     */
208
    public boolean isRepeatable() {
209
        return getTemplate().isRepeatableStatement(statementId);
×
210
    }
211

212
    /**
213
     * Checks if this statement item has empty elements.
214
     *
215
     * @return true if any of the repetition groups has empty elements, false otherwise
216
     */
217
    public boolean hasEmptyElements() {
218
        return repetitionGroups.get(0).hasEmptyElements();
×
219
    }
220

221
    /**
222
     * Returns the set of IRIs associated with this statement item.
223
     *
224
     * @return a set of IRIs
225
     */
226
    public Set<IRI> getIriSet() {
227
        return iriSet;
×
228
    }
229

230
    /**
231
     * Checks if this statement item will match any triple.
232
     *
233
     * @return true if it will match any triple, false otherwise
234
     */
235
    public boolean willMatchAnyTriple() {
236
        return repetitionGroups.get(0).matches(dummyStatementList);
×
237
    }
238

239
    /**
240
     * Fills this statement item with the provided list of statements, matching them against the repetition groups.
241
     *
242
     * @param statements the list of statements to match against
243
     * @throws com.knowledgepixels.nanodash.template.UnificationException if the statements cannot be unified with this statement item
244
     */
245
    public void fill(List<Statement> statements) throws UnificationException {
246
        if (isMatched) return;
×
247
        if (repetitionGroups.size() == 1) {
×
248
            RepetitionGroup rg = repetitionGroups.get(0);
×
249
            if (rg.matches(statements)) {
×
250
                rg.fill(statements);
×
251
            } else {
252
                return;
×
253
            }
254
        } else {
×
255
            return;
×
256
        }
257
        isMatched = true;
×
258
        if (!isRepeatable()) return;
×
259
        while (true) {
260
            RepetitionGroup newGroup = new RepetitionGroup();
×
261
            if (newGroup.matches(statements)) {
×
262
                newGroup.fill(statements);
×
263
                addRepetitionGroup(newGroup);
×
264
            } else {
265
                newGroup.disconnect();
×
266
                return;
×
267
            }
268
        }
×
269
    }
270

271
    /**
272
     * Marks the filling of this statement item as finished, indicating that all values have been filled.
273
     */
274
    public void fillFinished() {
275
        for (RepetitionGroup rg : repetitionGroups) {
×
276
            rg.fillFinished();
×
277
        }
×
278
    }
×
279

280
    /**
281
     * Finalizes the values of all ValueItems in this statement item.
282
     */
283
    public void finalizeValues() {
284
        for (RepetitionGroup rg : repetitionGroups) {
×
285
            rg.finalizeValues();
×
286
        }
×
287
    }
×
288

289
    /**
290
     * Returns true if the statement item has been matched with a set of statements.
291
     *
292
     * @return true if matched, false otherwise
293
     */
294
    public boolean isMatched() {
295
        return isMatched;
×
296
    }
297

298
    /**
299
     * Checks if this statement item is empty, meaning it has no filled repetition groups.
300
     *
301
     * @return true if the statement item is empty, false otherwise
302
     */
303
    public boolean isEmpty() {
304
        return repetitionGroups.size() == 1 && repetitionGroups.get(0).isEmpty();
×
305
    }
306

307
    /**
308
     * Represents a group of repetitions for a statement item, containing multiple statement parts.
309
     */
310
    public class RepetitionGroup implements Serializable {
311

312
        private List<StatementPartItem> statementParts;
313
        private List<ValueItem> localItems = new ArrayList<>();
×
314
        private boolean filled = false;
×
315

316
        private List<ValueItem> items = new ArrayList<>();
×
317

318
        Label addRepetitionButton, removeRepetitionButton, optionalMark;
319

320
        /**
321
         * Constructor for creating a RepetitionGroup.
322
         */
323
        public RepetitionGroup() {
×
324
            statementParts = new ArrayList<>();
×
325
            for (IRI s : statementPartIds) {
×
326
                StatementPartItem statement = new StatementPartItem("statement",
×
327
                        makeValueItem("subj", getTemplate().getSubject(s), s),
×
328
                        makeValueItem("pred", getTemplate().getPredicate(s), s),
×
329
                        makeValueItem("obj", getTemplate().getObject(s), s)
×
330
                );
331
                statementParts.add(statement);
×
332

333
                // Some of the methods of StatementItem and RepetitionGroup don't work properly before this
334
                // object is fully instantiated:
335
                boolean isFirstGroup = repetitionGroups.isEmpty();
×
336
                boolean isFirstLine = statementParts.size() == 1;
×
337
                boolean isLastLine = statementParts.size() == statementPartIds.size();
×
338
                boolean isOptional = getTemplate().isOptionalStatement(statementId);
×
339

340
                if (statementParts.size() == 1 && !isFirstGroup) {
×
341
                    statement.add(new AttributeAppender("class", " separate-statement"));
×
342
                }
343

344
                // This code adds "advanced" marks similar to "optional":
345
//                if (!context.isReadOnly()) {
346
//                    if (isOptional && isLastLine) {
347
//                        if (isAdvanced()) {
348
//                            optionalMark = new Label("label", "(optional, advanced)");
349
//                        } else {
350
//                            optionalMark = new Label("label", "(optional)");
351
//                        }
352
//                    } else if (isAdvanced()) {
353
//                        optionalMark = new Label("label", "(advanced)");
354
//                    } else {
355
//                        optionalMark = new Label("label", "");
356
//                        optionalMark.setVisible(false);
357
//                    }
358
//                } else {
359
//                    optionalMark = new Label("label", "");
360
//                    optionalMark.setVisible(false);
361
//                }
362

363
                if (!context.isReadOnly() && isOptional && isLastLine) {
×
364
                    optionalMark = new Label("label", "(optional)");
×
365
                } else {
366
                    optionalMark = new Label("label", "");
×
367
                    optionalMark.setVisible(false);
×
368
                }
369
                statement.add(optionalMark);
×
370
                if (isLastLine) {
×
371
                    addRepetitionButton = new Label("add-repetition", "+");
×
372
                    statement.add(addRepetitionButton);
×
373
                    addRepetitionButton.add(new AjaxEventBehavior("click") {
×
374

375
                        @Override
376
                        protected void onEvent(AjaxRequestTarget target) {
377
                            addRepetitionGroup(new RepetitionGroup());
×
378
                            target.add(StatementItem.this);
×
379
                            target.appendJavaScript("updateElements();");
×
380
                        }
×
381

382
                    });
383
                } else {
384
                    statement.add(new Label("add-repetition", "").setVisible(false));
×
385
                }
386
                if (isFirstLine) {
×
387
                    removeRepetitionButton = new Label("remove-repetition", "-");
×
388
                    statement.add(removeRepetitionButton);
×
389
                    removeRepetitionButton.add(new AjaxEventBehavior("click") {
×
390

391
                        @Override
392
                        protected void onEvent(AjaxRequestTarget target) {
393
                            RepetitionGroup.this.remove();
×
394
                            target.appendJavaScript("updateElements();");
×
395
                            target.add(StatementItem.this);
×
396
                        }
×
397

398
                    });
399
                } else {
400
                    statement.add(new Label("remove-repetition", "").setVisible(false));
×
401
                }
402
            }
×
403
        }
×
404

405
        private ValueItem makeValueItem(String id, Value value, IRI statementPartId) {
406
            if (isFirst() && value instanceof IRI) {
×
407
                iriSet.add((IRI) value);
×
408
            }
409
            ValueItem vi = new ValueItem(id, transform(value), statementPartId, this);
×
410
            localItems.add(vi);
×
411
            items.add(vi);
×
412
            return vi;
×
413
        }
414

415
        private void disconnect() {
416
            for (ValueItem vi : new ArrayList<>(localItems)) {
×
417
                // TODO These remove operations on list are slow. Improve:
418
                localItems.remove(vi);
×
419
                items.remove(vi);
×
420
                vi.removeFromContext();
×
421
            }
×
422
        }
×
423

424
        /**
425
         * Returns the statement parts.
426
         *
427
         * @return a list of StatementPartItem objects representing the statement parts
428
         */
429
        public List<StatementPartItem> getStatementParts() {
430
            return statementParts;
×
431
        }
432

433
        /**
434
         * Returns the index of this repetition group in the list of repetition groups.
435
         *
436
         * @return the index of this repetition group
437
         */
438
        public int getRepeatIndex() {
439
            if (!repetitionGroups.contains(this)) return repetitionGroups.size();
×
440
            return repetitionGroups.indexOf(this);
×
441
        }
442

443
        /**
444
         * Returns true if the repeat index if the first one.
445
         *
446
         * @return true if the repeat index is 0, false otherwise
447
         */
448
        public boolean isFirst() {
449
            return getRepeatIndex() == 0;
×
450
        }
451

452
        /**
453
         * Returns true if the repeat index is the last one.
454
         *
455
         * @return true if the repeat index is the last one, false otherwise
456
         */
457
        public boolean isLast() {
458
            return getRepeatIndex() == repetitionGroups.size() - 1;
×
459
        }
460

461
        private void remove() {
462
            String thisSuffix = getRepeatSuffix();
×
463
            for (IRI iriBase : iriSet) {
×
464
                IRI thisIri = vf.createIRI(iriBase + thisSuffix);
×
465
                if (context.getComponentModels().containsKey(thisIri)) {
×
466
                    IModel swapModel1 = (IModel) context.getComponentModels().get(thisIri);
×
467
                    for (int i = getRepeatIndex() + 1; i < repetitionGroups.size(); i++) {
×
468
                        IModel swapModel2 = (IModel) context.getComponentModels().get(vf.createIRI(iriBase + getRepeatSuffix(i)));
×
469
                        if (swapModel1 != null && swapModel2 != null) {
×
470
                            swapModel1.setObject(swapModel2.getObject());
×
471
                        }
472
                        // Drop any retained rawInput so the shifted model value is rendered
473
                        // instead of the user's previous (post-validation-error) entry.
474
                        clearInputForModel(swapModel1);
×
475
                        swapModel1 = swapModel2;
×
476
                    }
477
                    if (swapModel1 != null) {
×
478
                        swapModel1.setObject(null);
×
479
                        clearInputForModel(swapModel1);
×
480
                    }
481
                }
482
            }
×
483
            RepetitionGroup lastGroup = repetitionGroups.get(repetitionGroups.size() - 1);
×
484
            repetitionGroups.remove(lastGroup);
×
485
            for (ValueItem vi : lastGroup.items) {
×
486
                vi.removeFromContext();
×
487
            }
×
488
            repetitionGroupsChanged = true;
×
489
        }
×
490

491
        private void clearInputForModel(IModel<?> model) {
492
            if (model == null) return;
×
493
            for (Component c : context.getComponents()) {
×
494
                if (c instanceof FormComponent && c.getDefaultModel() == model) {
×
495
                    ((FormComponent<?>) c).clearInput();
×
496
                }
497
            }
×
498
        }
×
499

500
        private String getRepeatSuffix() {
501
            return getRepeatSuffix(getRepeatIndex());
×
502
        }
503

504
        private String getRepeatSuffix(int i) {
505
            if (i == 0) return "";
×
506
            // TODO: Check that this double-underscore pattern isn't used otherwise:
507
            return "__" + i;
×
508
        }
509

510
        /**
511
         * Returns the template context associated.
512
         *
513
         * @return the TemplateContext
514
         */
515
        public TemplateContext getContext() {
516
            return context;
×
517
        }
518

519
        /**
520
         * Checks if this repetition group is optional.
521
         *
522
         * @return true if the repetition group is optional, false otherwise
523
         */
524
        public boolean isOptional() {
525
            if (!getTemplate().isOptionalStatement(statementId)) return false;
×
526
            if (repetitionGroups.size() == 0) return true;
×
527
            if (repetitionGroups.size() == 1 && repetitionGroups.get(0) == this) return true;
×
528
            return false;
×
529
        }
530

531
        private Value transform(Value value) {
532
            if (!(value instanceof IRI)) {
×
533
                return value;
×
534
            }
535
            IRI iri = (IRI) value;
×
536
            String iriString = iri.stringValue();
×
537
            iriString = iriString.replaceAll("~~ARTIFACTCODE~~", "~~~ARTIFACTCODE~~~");
×
538
            // Only add "__N" to URI from second repetition group on; for the first group, information about
539
            // narrow scopes is not yet complete.
540
            if (getRepeatIndex() > 0 && context.hasNarrowScope(iri)) {
×
541
                if (context.getTemplate().isPlaceholder(iri) || context.getTemplate().isLocalResource(iri)) {
×
542
                    iriString += getRepeatSuffix();
×
543
                }
544
            }
545
            return vf.createIRI(iriString);
×
546
        }
547

548
        /**
549
         * Adds the triples of this repetition group to the given NanopubCreator.
550
         *
551
         * @param npCreator the NanopubCreator to which the triples will be added
552
         * @throws org.nanopub.NanopubAlreadyFinalizedException if the NanopubCreator has already been finalized
553
         */
554
        public void addTriplesTo(NanopubCreator npCreator) throws NanopubAlreadyFinalizedException {
555
            Template t = getTemplate();
×
556
            for (IRI s : statementPartIds) {
×
557
                IRI subj = context.processIri((IRI) transform(t.getSubject(s)));
×
558
                IRI pred = context.processIri((IRI) transform(t.getPredicate(s)));
×
559
                Value obj = context.processValue(transform(t.getObject(s)));
×
560
                if (context.getType() == ContextType.ASSERTION) {
×
561
                    npCreator.addAssertionStatement(subj, pred, obj);
×
562
                } else if (context.getType() == ContextType.PROVENANCE) {
×
563
                    npCreator.addProvenanceStatement(subj, pred, obj);
×
564
                } else if (context.getType() == ContextType.PUBINFO) {
×
565
                    npCreator.addPubinfoStatement(subj, pred, obj);
×
566
                }
567
            }
×
568
            for (ValueItem vi : items) {
×
569
                if (vi.getComponent() instanceof GuidedChoiceItem) {
×
570
                    String value = ((GuidedChoiceItem) vi.getComponent()).getModel().getObject();
×
571
                    if (value != null && GuidedChoiceItem.getLabel(value) != null) {
×
572
                        String label = GuidedChoiceItem.getLabel(value);
×
573
                        if (label.length() > 1000) label = label.substring(0, 997) + "...";
×
574
                        try {
575
                            npCreator.addPubinfoStatement(vf.createIRI(value), NTEMPLATE.HAS_LABEL_FROM_API, vf.createLiteral(label));
×
576
                        } catch (IllegalArgumentException ex) {
×
577
                            logger.error("Could not create IRI from value: {}", value, ex);
×
578
                        }
×
579
                    }
580
                }
581
            }
×
582
        }
×
583

584
        private boolean hasEmptyElements() {
585
            for (IRI s : statementPartIds) {
×
586
                if (context.processIri((IRI) transform(getTemplate().getSubject(s))) == null) return true;
×
587
                if (context.processIri((IRI) transform(getTemplate().getPredicate(s))) == null) return true;
×
588
                if (context.processValue(transform(getTemplate().getObject(s))) == null) return true;
×
589
            }
×
590
            return false;
×
591
        }
592

593
        /**
594
         * Checks if this repetition group is empty, meaning it has no filled items.
595
         *
596
         * @return true if the repetition group is empty, false otherwise
597
         */
598
        public boolean isEmpty() {
599
            for (IRI s : statementPartIds) {
×
600
                Template t = getTemplate();
×
601
                IRI subj = t.getSubject(s);
×
602
                if (t.isPlaceholder(subj) && context.hasNarrowScope(subj) && context.processIri((IRI) transform(subj)) != null)
×
603
                    return false;
×
604
                IRI pred = t.getPredicate(s);
×
605
                if (t.isPlaceholder(pred) && context.hasNarrowScope(pred) && context.processIri((IRI) transform(pred)) != null)
×
606
                    return false;
×
607
                Value obj = t.getObject(s);
×
608
                if (obj instanceof IRI && t.isPlaceholder((IRI) obj) && context.hasNarrowScope((IRI) obj) && context.processValue(transform(obj)) != null)
×
609
                    return false;
×
610
            }
×
611
            return true;
×
612
        }
613

614
        /**
615
         * Checks if this repetition group matches the provided list of statements.
616
         *
617
         * @param statements the list of statements to match against
618
         * @return true if the repetition group matches, false otherwise
619
         */
620
        public boolean matches(List<Statement> statements) {
621
            if (filled) return false;
×
622
            List<Statement> st = new ArrayList<>(statements);
×
623
            for (StatementPartItem p : statementParts) {
×
624
                Statement matchedStatement = null;
×
625
                for (Statement s : st) {
×
626
                    if (
×
627
                            p.getPredicate().isUnifiableWith(s.getPredicate()) &&  // checking predicate first optimizes performance
×
628
                            p.getSubject().isUnifiableWith(s.getSubject()) &&
×
629
                            p.getObject().isUnifiableWith(s.getObject())) {
×
630
                        matchedStatement = s;
×
631
                        break;
×
632
                    }
633
                }
×
634
                if (matchedStatement == null) {
×
635
                    return false;
×
636
                } else {
637
                    st.remove(matchedStatement);
×
638
                }
639
            }
×
640
            return true;
×
641
        }
642

643
        /**
644
         * Fills this repetition group with the provided list of statements, unifying them with the statement parts.
645
         *
646
         * @param statements the list of statements to match against
647
         * @throws UnificationException if the statements cannot be unified with this repetition group
648
         */
649
        public void fill(List<Statement> statements) throws UnificationException {
650
            if (filled) throw new UnificationException("Already filled");
×
651
            for (StatementPartItem p : statementParts) {
×
652
                Statement matchedStatement = null;
×
653
                for (Statement s : statements) {
×
654
                    if (
×
655
                            p.getPredicate().isUnifiableWith(s.getPredicate()) &&  // checking predicate first optimizes performance
×
656
                            p.getSubject().isUnifiableWith(s.getSubject()) &&
×
657
                            p.getObject().isUnifiableWith(s.getObject())) {
×
658
                        p.getPredicate().unifyWith(s.getPredicate());
×
659
                        p.getSubject().unifyWith(s.getSubject());
×
660
                        p.getObject().unifyWith(s.getObject());
×
661
                        matchedStatement = s;
×
662
                        break;
×
663
                    }
664
                }
×
665
                if (matchedStatement == null) {
×
666
                    throw new UnificationException("Unification seemed to work but then didn't");
×
667
                } else {
668
                    statements.remove(matchedStatement);
×
669
                }
670
                filled = true;
×
671
            }
×
672
        }
×
673

674
        /**
675
         * Marks the filling of this repetition group as finished, indicating that all values have been filled.
676
         */
677
        public void fillFinished() {
678
            for (ValueItem vi : items) {
×
679
                vi.fillFinished();
×
680
            }
×
681
        }
×
682

683
        /**
684
         * Finalizes the values of all ValueItems in this repetition group.
685
         */
686
        public void finalizeValues() {
687
            for (ValueItem vi : items) {
×
688
                vi.finalizeValues();
×
689
            }
×
690
        }
×
691

692
    }
693

694
    private static final ValueFactory vf = SimpleValueFactory.getInstance();
×
695
    private static final List<Statement> dummyStatementList = new ArrayList<Statement>(Collections.singletonList(vf.createStatement(vf.createIRI("http://dummy.com/"), vf.createIRI("http://dummy.com/"), vf.createIRI("http://dummy.com/"))));
×
696

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