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

knowledgepixels / nanodash / 30350726166

28 Jul 2026 10:25AM UTC coverage: 33.442% (+3.1%) from 30.298%
30350726166

push

github

web-flow
Merge pull request #567 from knowledgepixels/feat/reject-literals-in-subject-predicate

Report and block templates with literals in subject/predicate position

2337 of 7695 branches covered (30.37%)

Branch coverage included in aggregate %.

4532 of 12845 relevant lines covered (35.28%)

5.43 hits per line

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

85.09
src/main/java/com/knowledgepixels/nanodash/component/ValueItem.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.component.PublishForm.FillMode;
4
import com.knowledgepixels.nanodash.component.StatementItem.RepetitionGroup;
5
import com.knowledgepixels.nanodash.template.Template;
6
import com.knowledgepixels.nanodash.template.UnificationException;
7
import org.apache.wicket.Component;
8
import org.apache.wicket.ajax.AjaxRequestTarget;
9
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
10
import org.eclipse.rdf4j.model.IRI;
11
import org.eclipse.rdf4j.model.Literal;
12
import org.eclipse.rdf4j.model.Statement;
13
import org.eclipse.rdf4j.model.Value;
14
import org.eclipse.rdf4j.model.vocabulary.XSD;
15
import org.nanopub.Nanopub;
16
import org.nanopub.vocabulary.NPX;
17
import org.nanopub.vocabulary.NTEMPLATE;
18

19
/**
20
 * ValueItem is a panel that represents a single value in a statement.
21
 */
22
public class ValueItem extends AbstractContextComponent {
23

24
    private ContextComponent component;
25
    private Value value;
26

27
    /**
28
     * Constructor for ValueItem.
29
     *
30
     * @param id              the component id
31
     * @param value           the value to be represented
32
     * @param statementPartId the IRI of the statement part this value belongs to
33
     * @param rg              the repetition group this value is part of
34
     */
35
    public ValueItem(String id, Value value, IRI statementPartId, RepetitionGroup rg) {
36
        super(id, rg.getContext());
15✔
37
        this.value = value;
9✔
38
        final Template template = this.context.getTemplate();
12✔
39
        if (value instanceof IRI) {
9✔
40
            IRI iri = (IRI) value;
9✔
41
            if (template.isSequenceElementPlaceholder(iri)) {
12!
42
                component = new SequenceElementItem("value", iri, rg.getRepeatIndex() + 1, this.context);
×
43
            } else if (iri.equals(NTEMPLATE.CREATOR_PLACEHOLDER) || template.isRootNanopubPlaceholder(iri)) {
24!
44
                // These are special placeholders that are always read-only
45
                component = new ReadonlyItem("value", id, iri, statementPartId, rg);
33✔
46
            } else if (this.context.isReadOnly()) {
12✔
47
                if (template.isPlaceholder(iri) || template.isIntroducedResource(iri) || template.isEmbeddedResource(iri)) {
36!
48
                    component = new ReadonlyItem("value", id, iri, statementPartId, rg);
33✔
49
                } else {
50
                    component = new IriItem("value", id, iri, id.equals("obj"), statementPartId, rg);
42✔
51
                }
52
            } else if (template.isRestrictedChoicePlaceholder(iri)) {
12✔
53
                component = new RestrictedChoiceItem("value", id, iri, rg.isOptionalPart(statementPartId), this.context);
42✔
54
            } else if (template.isAgentPlaceholder(iri)) {
12✔
55
                component = new AgentChoiceItem("value", id, iri, rg.isOptionalPart(statementPartId), this.context);
42✔
56
            } else if (template.isGuidedChoicePlaceholder(iri)) {
12✔
57
                component = new GuidedChoiceItem("value", id, iri, rg.isOptionalPart(statementPartId), this.context);
42✔
58
            } else if (template.isIntroducedResource(iri) && (this.context.getFillMode() == FillMode.SUPERSEDE || this.context.getFillMode() == FillMode.OVERRIDE)
48!
59
                    && introducesAnything(this.context.getFillSource())) {
9✔
60
                // An introduced resource keeps its IRI across versions, so it is pinned
61
                // read-only on supersede/override -- but only if the source nanopub
62
                // actually introduces something to pin. Otherwise (e.g. an optional
63
                // statement whose introduced identifier the old version didn't declare
64
                // yet) the field must stay fillable (issue #549).
65
                component = new ReadonlyItem("value", id, iri, statementPartId, rg);
33✔
66
            } else if (template.isUriPlaceholder(iri)) {
12✔
67
                component = new IriTextfieldItem("value", id, iri, rg.isOptionalPart(statementPartId), this.context);
42✔
68
            } else if (template.isLongLiteralPlaceholder(iri)) {
12✔
69
                component = new LiteralTextareaItem("value", iri, rg.isOptionalPart(statementPartId), this.context);
39✔
70
            } else if (template.isLiteralPlaceholder(iri)) {
12✔
71
                // TODO add all date time types
72
                if (XSD.DATE.equals(template.getDatatype(iri))) {
18!
73
                    component = new LiteralDateItem("value", iri, rg.isOptionalPart(statementPartId), this.context);
×
74
                } else if (XSD.DATETIME.equals(template.getDatatype(iri))) {
18!
75
                    component = new LiteralDateTimeItem("value", iri, rg.isOptionalPart(statementPartId), this.context);
×
76
                } else {
77
                    component = new LiteralTextfieldItem("value", iri, rg.isOptionalPart(statementPartId), this.context);
39✔
78
                }
79
            } else if (template.isPlaceholder(iri)) {
12!
80
                component = new ValueTextfieldItem("value", id, iri, rg.isOptionalPart(statementPartId), this.context);
×
81
            } else {
82
                component = new IriItem("value", id, iri, id.equals("obj"), statementPartId, rg);
39✔
83
            }
84
        } else if (value instanceof Literal literal) {
21✔
85
            component = new LiteralItem("value", id, literal, rg);
30✔
86
        } else {
87
            // The template declares no value at all for this position (e.g. a statement
88
            // without rdf:subject). Render a marker rather than failing to render the whole
89
            // form; Template.getStatementErrors() reports what is missing.
90
            component = new MissingValueItem("value");
18✔
91
        }
92
        add((Component) component);
33✔
93
    }
3✔
94

95
    private static boolean introducesAnything(Nanopub np) {
96
        if (np == null) return false;
12✔
97
        for (Statement st : np.getPubinfo()) {
33✔
98
            if (st.getSubject().equals(np.getUri()) && st.getPredicate().equals(NPX.INTRODUCES) && st.getObject() instanceof IRI) {
45!
99
                return true;
6✔
100
            }
101
        }
3✔
102
        return false;
6✔
103
    }
104

105
    /**
106
     * OnChangeAjaxBehavior that keeps the value after a refresh.
107
     */
108
    public static class KeepValueAfterRefreshBehavior extends OnChangeAjaxBehavior {
9✔
109

110
        @Override
111
        protected void onUpdate(AjaxRequestTarget target) {
112
            // No actual action needed here; Ajax request alone ensures values are kept after refreshing.
113
        }
×
114

115
    }
116

117
    /**
118
     * {@inheritDoc}
119
     */
120
    @Override
121
    public void removeFromContext() {
122
        component.removeFromContext();
9✔
123
    }
3✔
124

125
    /**
126
     * {@inheritDoc}
127
     */
128
    @Override
129
    public boolean isUnifiableWith(Value v) {
130
        return component.isUnifiableWith(v);
15✔
131
    }
132

133
    /**
134
     * {@inheritDoc}
135
     */
136
    @Override
137
    public void unifyWith(Value v) throws UnificationException {
138
        component.unifyWith(v);
12✔
139
    }
3✔
140

141
    /**
142
     * {@inheritDoc}
143
     */
144
    @Override
145
    public void fillFinished() {
146
        component.fillFinished();
9✔
147
    }
3✔
148

149
    /**
150
     * {@inheritDoc}
151
     */
152
    @Override
153
    public void finalizeValues() {
154
        component.finalizeValues();
9✔
155
    }
3✔
156

157
    /**
158
     * Get the value represented by this ValueItem.
159
     *
160
     * @return the value
161
     */
162
    public Value getValue() {
163
        return value;
×
164
    }
165

166
    /**
167
     * Get the component that represents this ValueItem.
168
     *
169
     * @return the ContextComponent that represents this ValueItem
170
     */
171
    public ContextComponent getComponent() {
172
        return component;
9✔
173
    }
174

175
    /**
176
     * <p>toString.</p>
177
     *
178
     * @return a {@link java.lang.String} object
179
     */
180
    public String toString() {
181
        return component.toString();
×
182
    }
183

184
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc