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

knowledgepixels / nanodash / 28569727260

02 Jul 2026 06:17AM UTC coverage: 28.06% (+0.03%) from 28.026%
28569727260

push

github

web-flow
Merge pull request #526 from knowledgepixels/feat/template-role-direction-pins

feat: emit role-direction pins in pubinfo from templates

1734 of 7047 branches covered (24.61%)

Branch coverage included in aggregate %.

3621 of 12037 relevant lines covered (30.08%)

4.45 hits per line

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

39.55
src/main/java/com/knowledgepixels/nanodash/template/ValueFiller.java
1
package com.knowledgepixels.nanodash.template;
2

3
import com.knowledgepixels.nanodash.LocalUri;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.component.GuidedChoiceItem;
6
import com.knowledgepixels.nanodash.component.PublishForm.FillMode;
7
import com.knowledgepixels.nanodash.vocabulary.KPXL_TERMS;
8
import org.eclipse.rdf4j.model.*;
9
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
10
import org.eclipse.rdf4j.model.vocabulary.DCTERMS;
11
import org.eclipse.rdf4j.model.vocabulary.RDF;
12
import org.eclipse.rdf4j.model.vocabulary.RDFS;
13
import org.nanopub.Nanopub;
14
import org.nanopub.NanopubUtils;
15
import org.nanopub.vocabulary.NPX;
16
import org.nanopub.vocabulary.NTEMPLATE;
17
import org.slf4j.Logger;
18
import org.slf4j.LoggerFactory;
19

20
import java.util.ArrayList;
21
import java.util.List;
22
import java.util.Set;
23

24
/**
25
 * ValueFiller is a utility class that processes a Nanopub and fills a TemplateContext.
26
 */
27
public class ValueFiller {
28

29
    private static ValueFactory vf = SimpleValueFactory.getInstance();
6✔
30

31
    private Nanopub fillNp;
32
    private FillMode fillMode;
33
    private List<Statement> unusedStatements = new ArrayList<>();
15✔
34
    private int initialSize;
35
    private boolean formMode;
36
    private static final Logger logger = LoggerFactory.getLogger(ValueFiller.class);
12✔
37

38
    /**
39
     * Constructor for ValueFiller.
40
     *
41
     * @param fillNp      the Nanopub to fill
42
     * @param contextType the type of context to fill
43
     * @param formMode    if true, the filler will adapt to form mode, filtering out certain statements
44
     */
45
    public ValueFiller(Nanopub fillNp, ContextType contextType, boolean formMode) {
46
        this(fillNp, contextType, formMode, null);
18✔
47
    }
3✔
48

49
    /**
50
     * Constructor for ValueFiller with specified fill mode.
51
     *
52
     * @param fillNp      the Nanopub to fill
53
     * @param contextType the type of context to fill
54
     * @param formMode    if true, the filler will adapt to form mode, filtering out certain statements
55
     * @param fillMode    the fill mode to use, can be null for default behavior
56
     */
57
    public ValueFiller(Nanopub fillNp, ContextType contextType, boolean formMode, FillMode fillMode) {
6✔
58
        this.fillNp = fillNp;
9✔
59
        this.formMode = formMode;
9✔
60
        this.fillMode = fillMode;
9✔
61
        Set<Statement> statements;
62
        if (contextType == ContextType.ASSERTION) {
9!
63
            statements = fillNp.getAssertion();
12✔
64
        } else if (contextType == ContextType.PROVENANCE) {
×
65
            statements = fillNp.getProvenance();
×
66
        } else {
67
            statements = fillNp.getPubinfo();
×
68
        }
69
        for (Statement st : statements) {
30✔
70
            Statement stT = transform(st);
12✔
71
            if (stT != null) unusedStatements.add(stT);
21!
72
        }
3✔
73
        unusedStatements.sort((st1, st2) -> {
12✔
74
            String st1s = st1.getSubject() + " " + st1.getPredicate() + " " + st1.getObject();
33✔
75
            String st2s = st2.getSubject() + " " + st2.getPredicate() + " " + st2.getObject();
33✔
76
            return st1s.compareTo(st2s);
12✔
77
        });
78
        initialSize = unusedStatements.size();
15✔
79
    }
3✔
80

81
    /**
82
     * Fills the TemplateContext with the unused statements.
83
     *
84
     * @param context the TemplateContext to fill
85
     */
86
    public void fill(TemplateContext context) {
87
        try {
88
            context.fill(unusedStatements);
12✔
89
        } catch (UnificationException ex) {
×
90
            logger.error("Could not fill template context", ex);
×
91
        }
3✔
92
    }
3✔
93

94
    /**
95
     * Checks if the Nanopub has any statements.
96
     *
97
     * @return true if there are statements, false otherwise
98
     */
99
    public boolean hasStatements() {
100
        return initialSize > 0;
18!
101
    }
102

103
    /**
104
     * Checks if there are any used statements in the Nanopub.
105
     *
106
     * @return true if there are used statements, false otherwise
107
     */
108
    public boolean hasUsedStatements() {
109
        return unusedStatements.size() < initialSize;
24!
110
    }
111

112
    /**
113
     * Checks if there are any unused statements in the Nanopub.
114
     *
115
     * @return true if there are unused statements, false otherwise
116
     */
117
    public boolean hasUnusedStatements() {
118
        return !unusedStatements.isEmpty();
21!
119
    }
120

121
    /**
122
     * Returns the list of unused statements in the Nanopub.
123
     *
124
     * @return the list of unused statements
125
     */
126
    public List<Statement> getUnusedStatements() {
127
        return unusedStatements;
9✔
128
    }
129

130
    /**
131
     * Removes a specific unused statement from the list.
132
     *
133
     * @param st the statement to remove
134
     */
135
    public void removeUnusedStatement(Statement st) {
136
        unusedStatements.remove(st);
×
137
    }
×
138

139
    /**
140
     * Removes unused statements based on the specified subject, predicate, and object.
141
     *
142
     * @param subj the subject to match, can be null
143
     * @param pred the predicate to match, can be null
144
     * @param obj  the object to match, can be null
145
     */
146
    public void removeUnusedStatements(IRI subj, IRI pred, Value obj) {
147
        for (Statement st : new ArrayList<>(unusedStatements)) {
×
148
            if (subj != null && !st.getSubject().equals(subj)) continue;
×
149
            if (pred != null && !st.getPredicate().equals(pred)) continue;
×
150
            if (obj != null && !st.getObject().equals(obj)) continue;
×
151
            unusedStatements.remove(st);
×
152
        }
×
153
    }
×
154

155
    Statement transform(Statement st) {
156
        if (formMode && st.getContext().equals(fillNp.getPubinfoUri())) {
30!
157
            IRI pred = st.getPredicate();
×
158
            // TODO We might want to filter some of these out afterwards in PublishForm, to be more precise:
159
            if (st.getSubject().equals(fillNp.getUri())) {
×
160
                if (pred.equals(DCTERMS.CREATED)) return null;
×
161
                if (pred.equals(NPX.SUPERSEDES)) return null;
×
162
                if (pred.equals(RDFS.LABEL)) return null;
×
163
                if (pred.equals(NPX.INTRODUCES)) return null;
×
164
                if (pred.equals(NPX.EMBEDS)) return null;
×
165
                if (pred.equals(NPX.WAS_CREATED_AT)) return null;
×
166
                if (pred.equals(NTEMPLATE.WAS_CREATED_FROM_TEMPLATE)) return null;
×
167
                if (pred.equals(NTEMPLATE.WAS_CREATED_FROM_PROVENANCE_TEMPLATE)) return null;
×
168
                if (pred.equals(NTEMPLATE.WAS_CREATED_FROM_PUBINFO_TEMPLATE)) return null;
×
169
                // Role-instantiation type auto-added by PublishForm alongside the direction pins (#525).
170
                if (pred.equals(NPX.HAS_NANOPUB_TYPE) && st.getObject().equals(KPXL_TERMS.ROLE_INSTANTIATION)) return null;
×
171
            }
172
            // Role-direction pins auto-added by PublishForm; the subject is the (arbitrary)
173
            // role predicate, not the nanopub, so this is checked outside the block above (#525).
174
            if (pred.equals(RDF.TYPE) && (st.getObject().equals(KPXL_TERMS.INVERSE_ROLE_PROPERTY)
×
175
                    || st.getObject().equals(KPXL_TERMS.REGULAR_ROLE_PROPERTY))) return null;
×
176
            if (pred.equals(NPX.HAS_ALGORITHM)) return null;
×
177
            if (pred.equals(NPX.HAS_PUBLIC_KEY)) return null;
×
178
            if (pred.equals(NPX.HAS_SIGNATURE)) return null;
×
179
            if (pred.equals(NPX.HAS_SIGNATURE_TARGET)) return null;
×
180
            if (pred.equals(NPX.SIGNED_BY)) return null;
×
181
            if (pred.equals(NTEMPLATE.HAS_LABEL_FROM_API)) {
×
182
                GuidedChoiceItem.setLabel(st.getSubject().stringValue(), st.getObject().stringValue());
×
183
                return null;
×
184
            }
185
            if (pred.equals(RDFS.LABEL)) {
×
186
                GuidedChoiceItem.setLabel(st.getSubject().stringValue(), st.getObject().stringValue());
×
187
            }
188
        }
189
        return vf.createStatement(
15✔
190
                (Resource) transform(st.getSubject()),
15✔
191
                (IRI) transform(st.getPredicate()),
15✔
192
                transform(st.getObject()),
12✔
193
                (Resource) transform(st.getContext()));
9✔
194
    }
195

196
    Value transform(Value v) {
197
        if (fillNp.getUri().equals(v)) {
18✔
198
            return LocalUri.of("nanopub");
9✔
199
        } else if (fillNp.getAssertionUri().equals(v)) {
18✔
200
            return LocalUri.of("assertion");
9✔
201
        } else if (v instanceof IRI iri && formMode) {
27✔
202
            Set<String> introducedIriIds = NanopubUtils.getIntroducedIriIds(fillNp);
12✔
203
            Set<String> embeddedIriIds = NanopubUtils.getEmbeddedIriIds(fillNp);
12✔
204
            boolean isIntroduced = introducedIriIds.contains(iri.stringValue());
15✔
205
            boolean isEmbedded = embeddedIriIds.contains(iri.stringValue());
15✔
206
            // When superseding, only introduced IRIs should not be transformed.
207
            // Embedded IRIs should always be transformed (like when deriving).
208
            if (!isIntroduced || fillMode != FillMode.SUPERSEDE || isEmbedded) {
18!
209
                if (v.stringValue().startsWith(fillNp.getUri().stringValue())) {
24✔
210
                    return LocalUri.of(Utils.getUriPostfix(v.stringValue()));
15✔
211
                }
212
            }
213
        }
214
        return v;
6✔
215
    }
216

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