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

knowledgepixels / nanodash / 17760079101

16 Sep 2025 08:42AM UTC coverage: 13.879% (+0.08%) from 13.799%
17760079101

push

github

ashleycaselli
refactor: replace hardcoded local URI prefix with LocalUri constant and checks with Utils methods

443 of 4012 branches covered (11.04%)

Branch coverage included in aggregate %.

1126 of 7293 relevant lines covered (15.44%)

0.68 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/GuidedChoiceItem.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.LocalUri;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.component.IriTextfieldItem.Validator;
6
import com.knowledgepixels.nanodash.template.Template;
7
import com.knowledgepixels.nanodash.template.TemplateContext;
8
import com.knowledgepixels.nanodash.template.UnificationException;
9
import org.apache.wicket.Component;
10
import org.apache.wicket.ajax.AjaxRequestTarget;
11
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
12
import org.apache.wicket.behavior.AttributeAppender;
13
import org.apache.wicket.markup.html.basic.Label;
14
import org.apache.wicket.markup.html.link.ExternalLink;
15
import org.apache.wicket.markup.html.panel.Panel;
16
import org.apache.wicket.model.IModel;
17
import org.apache.wicket.model.Model;
18
import org.apache.wicket.validation.Validatable;
19
import org.eclipse.rdf4j.model.IRI;
20
import org.eclipse.rdf4j.model.Value;
21
import org.eclipse.rdf4j.model.ValueFactory;
22
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
23
import org.slf4j.Logger;
24
import org.slf4j.LoggerFactory;
25
import org.wicketstuff.select2.ChoiceProvider;
26
import org.wicketstuff.select2.Response;
27
import org.wicketstuff.select2.Select2Choice;
28

29
import java.util.*;
30

31
/**
32
 * A guided choice item that allows users to select from a list of predefined values.
33
 */
34
public class GuidedChoiceItem extends Panel implements ContextComponent {
35

36
    private static final long serialVersionUID = 1L;
37
    private TemplateContext context;
38
    private Select2Choice<String> textfield;
39
    private ExternalLink tooltipLink;
40
    private Label tooltipDescription;
41
    private IRI iri;
42
    private IModel<String> model;
43
    private static final Logger logger = LoggerFactory.getLogger(GuidedChoiceItem.class);
×
44

45
    private String prefix;
46

47
    // TODO: This map being static could mix up labels if the same URI is described at different places:
48
    // TODO: This should maybe go into a different class?
49
    private static Map<String, String> labelMap = new HashMap<>();
×
50

51
    /**
52
     * Get the label for a given value.
53
     *
54
     * @param value The value for which to get the label.
55
     * @return The label associated with the value, or null if not found.
56
     */
57
    public static String getLabel(String value) {
58
        return labelMap.get(value);
×
59
    }
60

61
    /**
62
     * Set a label for a given value.
63
     *
64
     * @param value The value for which to set the label.
65
     * @param label The label to associate with the value.
66
     * @return The previous label associated with the value, or null if there was none.
67
     */
68
    public static String setLabel(String value, String label) {
69
        return labelMap.put(value, label);
×
70
    }
71

72
    private String getChoiceLabel(String choiceId) {
73
        Template template = context.getTemplate();
×
74
        String label = null;
×
75
        if (choiceId.matches("https?://.+") && template.getLabel(vf.createIRI(choiceId)) != null) {
×
76
            label = template.getLabel(vf.createIRI(choiceId));
×
77
        } else if (labelMap.containsKey(choiceId)) {
×
78
            label = labelMap.get(choiceId);
×
79
            if (label.length() > 160) label = label.substring(0, 157) + "...";
×
80
        }
81
        return label;
×
82
    }
83

84
    /**
85
     * Constructor for the GuidedChoiceItem.
86
     *
87
     * @param id       The Wicket component ID.
88
     * @param parentId The parent ID, used for context.
89
     * @param iriP     The IRI associated with this choice item.
90
     * @param optional Whether the choice is optional or required.
91
     * @param context  The template context containing the template and models.
92
     */
93
    public GuidedChoiceItem(String id, String parentId, final IRI iriP, boolean optional, final TemplateContext context) {
94
        super(id);
×
95
        this.context = context;
×
96
        this.iri = iriP;
×
97
        final Template template = context.getTemplate();
×
98
        model = context.getComponentModels().get(iri);
×
99
        if (model == null) {
×
100
            model = Model.of("");
×
101
            context.getComponentModels().put(iri, model);
×
102
        }
103
        String postfix = Utils.getUriPostfix(iri);
×
104
        if (context.hasParam(postfix)) {
×
105
            model.setObject(context.getParam(postfix));
×
106
        }
107
        final List<String> possibleValues = new ArrayList<>();
×
108
        for (Value v : template.getPossibleValues(iri)) {
×
109
            possibleValues.add(v.toString());
×
110
        }
×
111

112
        prefix = template.getPrefix(iri);
×
113
        if (prefix == null) prefix = "";
×
114
        String prefixLabel = template.getPrefixLabel(iri);
×
115
        Label prefixLabelComp;
116
        if (prefixLabel == null) {
×
117
            prefixLabelComp = new Label("prefix", "");
×
118
            prefixLabelComp.setVisible(false);
×
119
        } else {
120
            if (!prefixLabel.isEmpty() && parentId.equals("subj") && !prefixLabel.matches("https?://.*")) {
×
121
                // Capitalize first letter of label if at subject position:
122
                prefixLabel = prefixLabel.substring(0, 1).toUpperCase() + prefixLabel.substring(1);
×
123
            }
124
            prefixLabelComp = new Label("prefix", prefixLabel);
×
125
        }
126
        add(prefixLabelComp);
×
127
        String prefixTooltip = prefix;
×
128
        if (!prefix.isEmpty()) {
×
129
            prefixTooltip += "...";
×
130
        }
131
        add(new Label("prefixtooltiptext", prefixTooltip));
×
132

133
        ChoiceProvider<String> choiceProvider = new ChoiceProvider<String>() {
×
134

135
            private static final long serialVersionUID = 1L;
136

137
            @Override
138
            public String getDisplayValue(String choiceId) {
139
                if (choiceId == null || choiceId.isEmpty()) return "";
×
140
                String label = getChoiceLabel(choiceId);
×
141
                if (label == null || label.isBlank()) {
×
142
                    return choiceId;
×
143
                }
144
                return label + " (" + choiceId + ")";
×
145
            }
146

147
            @Override
148
            public String getIdValue(String object) {
149
                return object;
×
150
            }
151

152
            // Getting strange errors with Tomcat if this method is not overridden:
153
            @Override
154
            public void detach() {
155
            }
×
156

157
            @Override
158
            public void query(String term, int page, Response<String> response) {
159
                if (term == null) {
×
160
                    response.addAll(possibleValues);
×
161
                    return;
×
162
                }
163
                if (term.startsWith("https://") || term.startsWith("http://")) {
×
164
                    if (prefix == null || term.startsWith(prefix)) {
×
165
                        response.add(term);
×
166
                    }
167
                }
168
                Map<String, Boolean> alreadyAddedMap = new HashMap<>();
×
169
                term = term.toLowerCase();
×
170
                for (String s : possibleValues) {
×
171
                    if (s.toLowerCase().contains(term) || getDisplayValue(s).toLowerCase().contains(term)) {
×
172
                        response.add(s);
×
173
                        alreadyAddedMap.put(s, true);
×
174
                    }
175
                }
×
176
                for (String v : context.getTemplate().getPossibleValuesFromApi(iri, term, labelMap)) {
×
177
                    if (!alreadyAddedMap.containsKey(v)) response.add(v);
×
178
                }
×
179
            }
×
180

181
            @Override
182
            public Collection<String> toChoices(Collection<String> ids) {
183
                return ids;
×
184
            }
185

186
        };
187
        textfield = new Select2Choice<String>("textfield", model, choiceProvider);
×
188
        textfield.getSettings().getAjax(true).setDelay(500);
×
189
        textfield.getSettings().setCloseOnSelect(true);
×
190
        String placeholder = template.getLabel(iri);
×
191
        if (placeholder == null) placeholder = "";
×
192
        textfield.getSettings().setPlaceholder(placeholder);
×
193
        Utils.setSelect2ChoiceMinimalEscapeMarkup(textfield);
×
194
        textfield.getSettings().setAllowClear(true);
×
195

196
        if (!optional) textfield.setRequired(true);
×
197
        textfield.add(new AttributeAppender("class", " wide"));
×
198
        textfield.add(new Validator(iri, template, prefix, context));
×
199
        context.getComponents().add(textfield);
×
200

201
        tooltipDescription = new Label("description", new IModel<String>() {
×
202

203
            private static final long serialVersionUID = 1L;
204

205
            @Override
206
            public String getObject() {
207
                String obj = GuidedChoiceItem.this.getModel().getObject();
×
208
                if (obj == null || obj.isEmpty()) return "choose a value";
×
209
                String label = getChoiceLabel(GuidedChoiceItem.this.getModel().getObject());
×
210
                if (label == null || !label.contains(" - ")) return "";
×
211
                return label.substring(label.indexOf(" - ") + 3);
×
212
            }
213

214
        });
215
        tooltipDescription.setOutputMarkupId(true);
×
216
        add(tooltipDescription);
×
217

218
        tooltipLink = Utils.getUriLink("uri", model);
×
219
        tooltipLink.setOutputMarkupId(true);
×
220
        add(tooltipLink);
×
221

222
        textfield.add(new OnChangeAjaxBehavior() {
×
223

224
            private static final long serialVersionUID = 1L;
225

226
            @Override
227
            protected void onUpdate(AjaxRequestTarget target) {
228
                for (Component c : context.getComponents()) {
×
229
                    if (c == textfield) continue;
×
230
                    if (c.getDefaultModel() == textfield.getModel()) {
×
231
                        c.modelChanged();
×
232
                        target.add(c);
×
233
                    }
234
                }
×
235
                target.add(tooltipLink);
×
236
                target.add(tooltipDescription);
×
237
            }
×
238

239
        });
240
        add(textfield);
×
241
    }
×
242

243
    /**
244
     * Get the model associated with this choice item.
245
     *
246
     * @return The model containing the selected value.
247
     */
248
    public IModel<String> getModel() {
249
        return model;
×
250
    }
251

252
    /**
253
     * {@inheritDoc}
254
     */
255
    @Override
256
    public void removeFromContext() {
257
        context.getComponents().remove(textfield);
×
258
    }
×
259

260
    /**
261
     * {@inheritDoc}
262
     */
263
    @Override
264
    public boolean isUnifiableWith(Value v) {
265
        if (v == null) return true;
×
266
        if (v instanceof IRI) {
×
267
            String vs = v.stringValue();
×
268
            if (vs.startsWith(prefix)) vs = vs.substring(prefix.length());
×
269
            if (Utils.isLocalURI(vs)) vs = vs.replaceFirst("^" + LocalUri.PREFIX, "");
×
270
            Validatable<String> validatable = new Validatable<>(vs);
×
271
            if (context.getTemplate().isLocalResource(iri) && !Utils.isUriPostfix(vs)) {
×
272
                vs = Utils.getUriPostfix(vs);
×
273
            }
274
            new Validator(iri, context.getTemplate(), prefix, context).validate(validatable);
×
275
            if (!validatable.isValid()) {
×
276
                return false;
×
277
            }
278
            if (textfield.getModelObject().isEmpty()) {
×
279
                return true;
×
280
            }
281
            return vs.equals(textfield.getModelObject());
×
282
        }
283
        return false;
×
284
    }
285

286
    /**
287
     * {@inheritDoc}
288
     */
289
    @Override
290
    public void unifyWith(Value v) throws UnificationException {
291
        if (v == null) return;
×
292
        if (!isUnifiableWith(v)) throw new UnificationException(v.stringValue());
×
293
        String vs = v.stringValue();
×
294
        if (prefix != null && vs.startsWith(prefix)) {
×
295
            vs = vs.substring(prefix.length());
×
296
        } else if (Utils.isLocalURI(vs)) {
×
297
            vs = vs.replaceFirst("^" + LocalUri.PREFIX, "");
×
298
        }
299
        textfield.setModelObject(vs);
×
300
        // TODO: This should be done differently, at a different place (can slow down unification):
301
        if (!labelMap.containsKey(vs)) {
×
302
            context.getTemplate().getPossibleValuesFromApi(iri, vs, labelMap);
×
303
        }
304
    }
×
305

306
    /**
307
     * {@inheritDoc}
308
     */
309
    @Override
310
    public void fillFinished() {
311
    }
×
312

313
    /**
314
     * {@inheritDoc}
315
     */
316
    @Override
317
    public void finalizeValues() {
318
        Value defaultValue = context.getTemplate().getDefault(iri);
×
319
        if (isUnifiableWith(defaultValue)) {
×
320
            try {
321
                unifyWith(defaultValue);
×
322
            } catch (UnificationException ex) {
×
323
                logger.error("Could not unify default value: {}", defaultValue, ex);
×
324
            }
×
325
        }
326
    }
×
327

328
    private static ValueFactory vf = SimpleValueFactory.getInstance();
×
329

330
    /**
331
     * {@inheritDoc}
332
     */
333
    @Override
334
    public String toString() {
335
        return "[Guided choiced item: " + iri + "]";
×
336
    }
337

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