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

knowledgepixels / nanodash / 28856524011

07 Jul 2026 09:37AM UTC coverage: 28.465% (+0.01%) from 28.452%
28856524011

push

github

web-flow
Merge pull request #539 from knowledgepixels/fix/full-explicit-query-labels

fix: show explicit query labels in full instead of truncating

1813 of 7209 branches covered (25.15%)

Branch coverage included in aggregate %.

3715 of 12211 relevant lines covered (30.42%)

4.52 hits per line

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

43.46
src/main/java/com/knowledgepixels/nanodash/component/NanodashLink.java
1
package com.knowledgepixels.nanodash.component;
2

3
import com.knowledgepixels.nanodash.NavigationContext;
4
import com.knowledgepixels.nanodash.Utils;
5
import com.knowledgepixels.nanodash.domain.IndividualAgent;
6
import com.knowledgepixels.nanodash.domain.MaintainedResource;
7
import com.knowledgepixels.nanodash.domain.Space;
8
import com.knowledgepixels.nanodash.domain.User;
9
import com.knowledgepixels.nanodash.connector.ios.DsConfig;
10
import com.knowledgepixels.nanodash.connector.ios.DsNanopubPage;
11
import com.knowledgepixels.nanodash.connector.pensoft.BdjConfig;
12
import com.knowledgepixels.nanodash.connector.pensoft.BdjNanopubPage;
13
import com.knowledgepixels.nanodash.connector.pensoft.RioConfig;
14
import com.knowledgepixels.nanodash.connector.pensoft.RioNanopubPage;
15
import com.knowledgepixels.nanodash.page.ExplorePage;
16
import com.knowledgepixels.nanodash.page.MaintainedResourcePage;
17
import com.knowledgepixels.nanodash.page.ResourcePartPage;
18
import com.knowledgepixels.nanodash.page.SpacePage;
19
import com.knowledgepixels.nanodash.page.UserPage;
20
import com.knowledgepixels.nanodash.repository.MaintainedResourceRepository;
21
import com.knowledgepixels.nanodash.repository.SpaceRepository;
22
import com.knowledgepixels.nanodash.template.Template;
23
import com.knowledgepixels.nanodash.template.TemplateData;
24
import net.trustyuri.TrustyUriUtils;
25
import org.apache.commons.codec.Charsets;
26
import org.apache.wicket.Component;
27
import org.apache.wicket.behavior.AttributeAppender;
28
import org.apache.wicket.markup.html.basic.Label;
29
import org.apache.wicket.markup.html.link.BookmarkablePageLink;
30
import org.apache.wicket.markup.html.link.ExternalLink;
31
import org.apache.wicket.markup.html.panel.Panel;
32
import org.apache.wicket.model.Model;
33
import org.apache.wicket.request.mapper.parameter.PageParameters;
34
import org.eclipse.rdf4j.model.IRI;
35
import org.eclipse.rdf4j.model.Statement;
36
import org.eclipse.rdf4j.model.ValueFactory;
37
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
38
import org.eclipse.rdf4j.model.vocabulary.RDFS;
39
import org.nanopub.Nanopub;
40
import org.nanopub.vocabulary.NP;
41
import org.nanopub.vocabulary.NTEMPLATE;
42

43
import java.net.URLEncoder;
44
import java.util.*;
45

46
/**
47
 * A Wicket component that creates a link to a nanopublication or an IRI.
48
 */
49
public class NanodashLink extends Panel {
50

51
    public NanodashLink(String id, String uri, Nanopub np, IRI templateClass, String label) {
52
        this(id, uri, np, templateClass, label, null);
24✔
53
    }
3✔
54

55
    /**
56
     * Creates a link to a nanopublication or an IRI.
57
     *
58
     * @param id            the Wicket component ID
59
     * @param uri           the URI of the nanopublication or IRI
60
     * @param np            the nanopublication, or null if the link is not to a nanopublication
61
     * @param templateClass the template class of the nanopublication, or null if the link is not to a nanopublication
62
     * @param label         the label to display for the link, or null to derive it from the nanopublication or IRI
63
     */
64
    public NanodashLink(String id, String uri, Nanopub np, IRI templateClass, String label, String contextId) {
65
        super(id);
9✔
66

67
        final List<Template> templates = new ArrayList<>();
12✔
68
        final Map<IRI, String> labels = new HashMap<>();
12✔
69
        if (np != null) {
6!
70
            for (Statement st : np.getPubinfo()) {
33✔
71
                if (st.getPredicate().equals(NTEMPLATE.HAS_LABEL_FROM_API) || st.getPredicate().equals(RDFS.LABEL)) {
30!
72
                    labels.put((IRI) st.getSubject(), st.getObject().stringValue());
×
73
                }
74
            }
3✔
75
        }
76

77
        final TemplateData td = TemplateData.get();
6✔
78

79
        if (NTEMPLATE.ASSERTION_TEMPLATE.equals(templateClass)) {
12!
80
            IRI templateId = td.getTemplateId(np);
×
81
            if (templateId != null) {
×
82
                templates.add(td.getTemplate(templateId.stringValue()));
×
83
            }
84
        } else if (NTEMPLATE.PROVENANCE_TEMPLATE.equals(templateClass)) {
12!
85
            IRI templateId = td.getProvenanceTemplateId(np);
×
86
            if (templateId != null) {
×
87
                templates.add(td.getTemplate(templateId.stringValue()));
×
88
            }
89
        } else if (NTEMPLATE.PUBINFO_TEMPLATE.equals(templateClass)) {
12!
90
            Set<IRI> templateIds = td.getPubinfoTemplateIds(np);
×
91
            for (IRI templateId : templateIds) {
×
92
                templates.add(td.getTemplate(templateId.stringValue()));
×
93
            }
×
94
        }
95

96
        final IRI iriObj = vf.createIRI(uri);
12✔
97
        if (np != null && uri.equals(np.getUri().stringValue())) {
24!
98
            ExternalLink link = new ExternalLink("link", ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, Charsets.UTF_8), "this");
×
99
            add(link);
×
100
            add(new Label("description", "this specific nanopublication"));
×
101
        } else if (np != null && uri.equals(np.getAssertionUri().stringValue())) {
24!
102
            ExternalLink link = new ExternalLink("link", ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, Charsets.UTF_8), "this assertion");
×
103
            link.add(new AttributeAppender("class", " nanopub-assertion "));
×
104
            add(link);
×
105
            add(new Label("description", "the assertion of this specific nanopublication"));
×
106
        } else if (uri.equals(NP.HAS_ASSERTION.stringValue())) {
15!
107
            ExternalLink link = new ExternalLink("link", ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, Charsets.UTF_8), "assertion");
×
108
            link.add(new AttributeAppender("class", " nanopub-assertion "));
×
109
            add(link);
×
110
            add(new Label("description", "links a nanopublication to its assertion"));
×
111
        } else if (uri.equals(NP.HAS_PROVENANCE.stringValue())) {
15!
112
            ExternalLink link = new ExternalLink("link", ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, Charsets.UTF_8), "provenance");
×
113
            link.add(new AttributeAppender("class", " nanopub-provenance "));
×
114
            add(link);
×
115
            add(new Label("description", "links a nanopublication to its provenance"));
×
116
        } else if (uri.equals(NP.HAS_PUBINFO.stringValue())) {
15!
117
            ExternalLink link = new ExternalLink("link", ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, Charsets.UTF_8), "pubinfo");
×
118
            link.add(new AttributeAppender("class", " nanopub-pubinfo "));
×
119
            add(link);
×
120
            add(new Label("description", "links a nanopublication to its pubinfo"));
×
121
        } else {
×
122
            final boolean explicitLabel = label != null && !label.isBlank();
12!
123
            if (!explicitLabel) {
6!
124
                label = Utils.getShortNameFromURI(uri);
9✔
125
                if (iriObj.equals(User.getSignatureOwnerIri(np))) {
15!
126
                    // TODO We might want to introduce a "(you)" flag here at some point
127
                    label = User.getShortDisplayName(iriObj);
×
128
                } else if (User.getName(iriObj) != null) {
9!
129
                    label = User.getShortDisplayName(iriObj);
×
130
                } else if (SpaceRepository.get().findById(uri) != null) {
12!
131
                    label = SpaceRepository.get().findById(uri).getLabel();
×
132
                } else if (SpaceRepository.get().findByAltId(uri) != null) {
12!
133
                    label = SpaceRepository.get().findByAltId(uri).getLabel();
×
134
                } else if (MaintainedResourceRepository.get().findById(uri) != null) {
12!
135
                    label = MaintainedResourceRepository.get().findById(uri).getLabel();
×
136
                } else {
137
                    for (Template template : templates) {
18!
138
                        // TODO For pubinfo templates, we don't consider which triple came from which template (which is non-trivial):
139
                        String l = template.getLabel(iriObj);
×
140
                        if (l != null) {
×
141
                            label = l;
×
142
                            break;
×
143
                        }
144
                        l = labels.get(iriObj);
×
145
                        if (l != null) {
×
146
                            label = l;
×
147
                            break;
×
148
                        }
149
                    }
×
150
                }
151
            }
152
            String shortLabel = label.replaceFirst(" - [\\s\\S]*$", "");
15✔
153
            add(createLink("link", uri, shortLabel, contextId, explicitLabel));
42✔
154
            String description = "";
6✔
155
            if (np != null && uri.startsWith(np.getUri().stringValue())) {
24!
156
                description = "This is a local identifier that was minted when the nanopublication was created.";
×
157
            }
158
            if (label.contains(" - ")) description = label.replaceFirst("^.* - ", "");
12!
159
            add(new Label("description", description));
39✔
160
        }
161
        add(Utils.getUriLink("uri", uri));
33✔
162
    }
3✔
163

164
    /**
165
     * <p>createLink.</p>
166
     *
167
     * @param markupId a {@link java.lang.String} object
168
     * @param uri      a {@link java.lang.String} object
169
     * @param label    a {@link java.lang.String} object
170
     * @return a {@link org.apache.wicket.Component} object
171
     */
172
    public static Component createLink(String markupId, String uri, String label, String contextId) {
173
        return createLink(markupId, uri, label, contextId, false);
×
174
    }
175

176
    /**
177
     * Like {@link #createLink(String, String, String, String)}, but with a flag telling
178
     * whether the label was explicitly provided (e.g. returned by a query). Explicit
179
     * labels are shown in full; only derived labels are truncated for display.
180
     */
181
    public static Component createLink(String markupId, String uri, String label, String contextId, boolean explicitLabel) {
182
        Component link = createLinkComponent(markupId, uri, label, contextId, explicitLabel);
21✔
183
        // Fall back to the page's navigation context when the caller didn't supply one
184
        // (e.g. nanopub cards), so the target page's back-link still points back here.
185
        if (link instanceof BookmarkablePageLink) {
9!
186
            link.add(NavigationContext.pageContextFallback());
27✔
187
        }
188
        return link;
6✔
189
    }
190

191
    private static Component createLinkComponent(String markupId, String uri, String label, String contextId, boolean explicitLabel) {
192
        boolean isNp = TrustyUriUtils.isPotentialTrustyUri(uri);
9✔
193
        PageParameters params = new PageParameters().set("id", uri);
21✔
194
        if (contextId != null) params.set("context", contextId);
6!
195
        // TODO Improve this
196
        if (isNp && uri.startsWith(DsConfig.get().getTargetNamespace())) {
6!
197
            return new BookmarkablePageLink<Void>(markupId, DsNanopubPage.class, params.set("mode", "final")).setBody(Model.of(displayLabel(label, explicitLabel)));
×
198
        } else if (isNp && uri.startsWith(BdjConfig.get().getTargetNamespace())) {
6!
199
            return new BookmarkablePageLink<Void>(markupId, BdjNanopubPage.class, params.set("mode", "final")).setBody(Model.of(displayLabel(label, explicitLabel)));
×
200
        } else if (isNp && uri.startsWith(RioConfig.get().getTargetNamespace())) {
6!
201
            return new BookmarkablePageLink<Void>(markupId, RioNanopubPage.class, params.set("mode", "final")).setBody(Model.of(displayLabel(label, explicitLabel)));
×
202
        } else if (IndividualAgent.isUser(uri) || IndividualAgent.isOrcidIri(uri)) {
18!
203
            IRI userIri = vf.createIRI(uri);
×
204
            // Prefer our own known display name; otherwise keep a label supplied by the
205
            // caller (e.g. a name resolved by a query) before falling back to the bare id.
206
            if (User.getName(userIri) != null || label == null || label.isBlank()) {
×
207
                label = User.getShortDisplayName(userIri);
×
208
                explicitLabel = false;
×
209
            }
210
            return new BookmarkablePageLink<Void>(markupId, UserPage.class, params).setBody(Model.of(displayLabel(label, explicitLabel)));
×
211
        } else if (SpaceRepository.get().findById(uri) != null) {
12!
212
            label = SpaceRepository.get().findById(uri).getLabel();
×
213
            return new BookmarkablePageLink<Void>(markupId, SpacePage.class, params).setBody(Model.of(Utils.truncateLinkLabel(label)));
×
214
        } else if (SpaceRepository.get().findByAltId(uri) != null) {
12!
215
            Space space = SpaceRepository.get().findByAltId(uri);
×
216
            label = space.getLabel();
×
217
            params.set("id", space.getId());
×
218
            return new BookmarkablePageLink<Void>(markupId, SpacePage.class, params).setBody(Model.of(Utils.truncateLinkLabel(label)));
×
219
        } else if (MaintainedResourceRepository.get().findById(uri) != null) {
12!
220
            label = MaintainedResourceRepository.get().findById(uri).getLabel();
×
221
            return new BookmarkablePageLink<Void>(markupId, MaintainedResourcePage.class, params).setBody(Model.of(Utils.truncateLinkLabel(label)));
×
222
        } else {
223
            boolean isCaret = "^".equals(label);
12✔
224
            if (!isCaret) params.set("label", label);
21!
225
            BookmarkablePageLink<Void> link;
226
            if (isPartOfResource(uri, contextId)) {
12!
227
                link = new BookmarkablePageLink<Void>(markupId, ResourcePartPage.class, params);
×
228
            } else {
229
                link = new BookmarkablePageLink<Void>(markupId, ExplorePage.class, params.set("forward-to-part", "true"));
30✔
230
            }
231
            // Truncate only derived labels; the full label stays in the page
232
            // params above so the destination page can still show it as its title.
233
            link.setBody(Model.of(displayLabel(label, explicitLabel)));
21✔
234
            // The "^" source-nanopub caret is styled like the admin lists (subtle, with
235
            // hover-darken), reusing SourceNanopub's `a.source` styling.
236
            if (isCaret) link.add(new AttributeAppender("class", "source"));
6!
237
            return link;
6✔
238
        }
239
    }
240

241
    /**
242
     * Explicit labels (e.g. returned by a query) are shown in full; labels derived
243
     * from the URI or local repositories are truncated for display.
244
     */
245
    private static String displayLabel(String label, boolean explicit) {
246
        return explicit ? label : Utils.truncateLinkLabel(label);
15!
247
    }
248

249
    /**
250
     * Returns the best page URL for a given URI, routing to user/space/resource pages when applicable,
251
     * falling back to the Explore page otherwise.
252
     */
253
    public static String getPageUrl(String uri) {
254
        if (IndividualAgent.isUser(uri) || IndividualAgent.isOrcidIri(uri)) {
18!
255
            return UserPage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, java.nio.charset.StandardCharsets.UTF_8);
×
256
        } else if (SpaceRepository.get().findById(uri) != null) {
12!
257
            return SpacePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, java.nio.charset.StandardCharsets.UTF_8);
×
258
        } else if (SpaceRepository.get().findByAltId(uri) != null) {
12!
259
            String spaceId = SpaceRepository.get().findByAltId(uri).getId();
×
260
            return SpacePage.MOUNT_PATH + "?id=" + URLEncoder.encode(spaceId, java.nio.charset.StandardCharsets.UTF_8);
×
261
        } else if (MaintainedResourceRepository.get().findById(uri) != null) {
12!
262
            return MaintainedResourcePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, java.nio.charset.StandardCharsets.UTF_8);
×
263
        } else {
264
            return ExplorePage.MOUNT_PATH + "?id=" + URLEncoder.encode(uri, java.nio.charset.StandardCharsets.UTF_8);
15✔
265
        }
266
    }
267

268
    private static boolean isPartOfResource(String uri, String contextId) {
269
        if (contextId == null) return false;
12!
270
        String uriNamespace = MaintainedResource.getNamespace(uri);
×
271
        MaintainedResource resource = MaintainedResourceRepository.get().findByNamespace(uriNamespace);
×
272
        if (resource == null) return false;
×
273
        return resource.getId().equals(contextId);
×
274
    }
275

276
    /**
277
     * Creates a link to a nanopublication or an IRI.
278
     *
279
     * @param id            the Wicket component ID
280
     * @param uri           the URI of the nanopublication or IRI
281
     * @param np            the nanopublication, or null if the link is not to a nanopublication
282
     * @param templateClass the template class of the nanopublication, or null if the link is not to a nanopublication
283
     */
284
    public NanodashLink(String id, String uri, Nanopub np, IRI templateClass) {
285
        this(id, uri, np, templateClass, null);
21✔
286
    }
3✔
287

288
    /**
289
     * Creates a link to a nanopublication or an IRI.
290
     *
291
     * @param id  the Wicket component ID
292
     * @param uri the URI of the nanopublication or IRI
293
     * @param np  the nanopublication, or null if the link is not to a nanopublication
294
     */
295
    public NanodashLink(String id, String uri, Nanopub np) {
296
        this(id, uri, np, null);
×
297
    }
×
298

299
    /**
300
     * Creates a link to a nanopublication or an IRI.
301
     *
302
     * @param id  the Wicket component ID
303
     * @param uri the URI of the nanopublication or IRI
304
     */
305
    public NanodashLink(String id, String uri) {
306
        this(id, uri, null, null);
×
307
    }
×
308

309
    private static ValueFactory vf = SimpleValueFactory.getInstance();
9✔
310

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