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

knowledgepixels / nanodash / 21431284807

28 Jan 2026 08:43AM UTC coverage: 14.227% (-0.9%) from 15.111%
21431284807

push

github

tkuhn
feat: show label for profiled resources filled into form

548 of 5082 branches covered (10.78%)

Branch coverage included in aggregate %.

1500 of 9313 relevant lines covered (16.11%)

2.12 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.ProfiledResource;
5
import com.knowledgepixels.nanodash.Space;
6
import com.knowledgepixels.nanodash.Utils;
7
import com.knowledgepixels.nanodash.component.IriTextfieldItem.Validator;
8
import com.knowledgepixels.nanodash.template.Template;
9
import com.knowledgepixels.nanodash.template.TemplateContext;
10
import com.knowledgepixels.nanodash.template.UnificationException;
11
import org.apache.wicket.Component;
12
import org.apache.wicket.ajax.AjaxRequestTarget;
13
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
14
import org.apache.wicket.behavior.AttributeAppender;
15
import org.apache.wicket.markup.html.basic.Label;
16
import org.apache.wicket.markup.html.link.ExternalLink;
17
import org.apache.wicket.model.IModel;
18
import org.apache.wicket.model.Model;
19
import org.apache.wicket.validation.Validatable;
20
import org.eclipse.rdf4j.model.IRI;
21
import org.eclipse.rdf4j.model.Value;
22
import org.slf4j.Logger;
23
import org.slf4j.LoggerFactory;
24
import org.wicketstuff.select2.ChoiceProvider;
25
import org.wicketstuff.select2.Response;
26
import org.wicketstuff.select2.Select2Choice;
27

28
import java.util.*;
29

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

35
    private Select2Choice<String> textfield;
36
    private ExternalLink tooltipLink;
37
    private Label tooltipDescription;
38
    private IRI iri;
39
    private IModel<String> model;
40
    private static final Logger logger = LoggerFactory.getLogger(GuidedChoiceItem.class);
×
41

42
    private String prefix;
43

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

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

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

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

81
    /**
82
     * Constructor for the GuidedChoiceItem.
83
     *
84
     * @param id       The Wicket component ID.
85
     * @param parentId The parent ID, used for context.
86
     * @param iriP     The IRI associated with this choice item.
87
     * @param optional Whether the choice is optional or required.
88
     * @param context  The template context containing the template and models.
89
     */
90
    public GuidedChoiceItem(String id, String parentId, final IRI iriP, boolean optional, final TemplateContext context) {
91
        super(id, context);
×
92
        this.iri = iriP;
×
93
        final Template template = context.getTemplate();
×
94
        model = (IModel<String>) context.getComponentModels().get(iri);
×
95
        if (model == null) {
×
96
            model = Model.of("");
×
97
            context.getComponentModels().put(iri, model);
×
98
        }
99
        String postfix = Utils.getUriPostfix(iri);
×
100
        if (context.hasParam(postfix)) {
×
101
            String objId = context.getParam(postfix);
×
102
            if (ProfiledResource.get(objId) != null) {
×
103
                labelMap.put(objId, ProfiledResource.get(objId).getLabel());
×
104
            }
105
            model.setObject(objId);
×
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
            @Override
136
            public String getDisplayValue(String choiceId) {
137
                if (choiceId == null || choiceId.isEmpty()) return "";
×
138
                String label = getChoiceLabel(choiceId);
×
139
                if (label == null || label.isBlank()) {
×
140
                    return choiceId;
×
141
                }
142
                return label + " (" + choiceId + ")";
×
143
            }
144

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

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

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

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

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

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

199
        tooltipDescription = new Label("description", new IModel<String>() {
×
200

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

210
        });
211
        tooltipDescription.setOutputMarkupId(true);
×
212
        add(tooltipDescription);
×
213

214
        tooltipLink = Utils.getUriLink("uri", model);
×
215
        tooltipLink.setOutputMarkupId(true);
×
216
        add(tooltipLink);
×
217

218
        textfield.add(new OnChangeAjaxBehavior() {
×
219

220
            @Override
221
            protected void onUpdate(AjaxRequestTarget target) {
222
                for (Component c : context.getComponents()) {
×
223
                    if (c == textfield) continue;
×
224
                    if (c.getDefaultModel() == textfield.getModel()) {
×
225
                        c.modelChanged();
×
226
                        target.add(c);
×
227
                    }
228
                }
×
229
                target.add(tooltipLink);
×
230
                target.add(tooltipDescription);
×
231
            }
×
232

233
        });
234
        add(textfield);
×
235
    }
×
236

237
    /**
238
     * Get the model associated with this choice item.
239
     *
240
     * @return The model containing the selected value.
241
     */
242
    public IModel<String> getModel() {
243
        return model;
×
244
    }
245

246
    /**
247
     * {@inheritDoc}
248
     */
249
    @Override
250
    public void removeFromContext() {
251
        context.getComponents().remove(textfield);
×
252
    }
×
253

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

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

300
    /**
301
     * {@inheritDoc}
302
     */
303
    @Override
304
    public void fillFinished() {
305
    }
×
306

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

322
    /**
323
     * {@inheritDoc}
324
     */
325
    @Override
326
    public String toString() {
327
        return "[Guided choiced item: " + iri + "]";
×
328
    }
329

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