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

knowledgepixels / nanodash / 17302137193

28 Aug 2025 04:35PM UTC coverage: 11.965% (-0.4%) from 12.355%
17302137193

Pull #244

github

web-flow
Merge 4e969b0ee into 3323a35f1
Pull Request #244: Use vocabularies with latest version of `nanopub-java`

331 of 3840 branches covered (8.62%)

Branch coverage included in aggregate %.

943 of 6808 relevant lines covered (13.85%)

0.61 hits per line

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

51.79
src/main/java/com/knowledgepixels/nanodash/Utils.java
1
package com.knowledgepixels.nanodash;
2

3
import com.google.common.hash.Hashing;
4
import net.trustyuri.TrustyUriUtils;
5
import org.apache.commons.codec.Charsets;
6
import org.apache.commons.exec.environment.EnvironmentUtils;
7
import org.apache.commons.lang.StringUtils;
8
import org.apache.http.client.utils.URIBuilder;
9
import org.apache.wicket.markup.html.link.ExternalLink;
10
import org.apache.wicket.model.IModel;
11
import org.apache.wicket.request.mapper.parameter.PageParameters;
12
import org.apache.wicket.util.string.StringValue;
13
import org.eclipse.rdf4j.model.IRI;
14
import org.eclipse.rdf4j.model.Literal;
15
import org.eclipse.rdf4j.model.Statement;
16
import org.eclipse.rdf4j.model.ValueFactory;
17
import org.eclipse.rdf4j.model.base.CoreDatatype.XSD;
18
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
19
import org.eclipse.rdf4j.model.util.Literals;
20
import org.eclipse.rdf4j.model.vocabulary.FOAF;
21
import org.nanopub.Nanopub;
22
import org.nanopub.NanopubUtils;
23
import org.nanopub.extra.security.KeyDeclaration;
24
import org.nanopub.extra.security.MalformedCryptoElementException;
25
import org.nanopub.extra.security.NanopubSignatureElement;
26
import org.nanopub.extra.security.SignatureUtils;
27
import org.nanopub.extra.server.GetNanopub;
28
import org.nanopub.extra.services.ApiResponseEntry;
29
import org.nanopub.extra.setting.IntroNanopub;
30
import org.nanopub.vocabulary.FIP;
31
import org.nanopub.vocabulary.NPX;
32
import org.owasp.html.HtmlPolicyBuilder;
33
import org.owasp.html.PolicyFactory;
34
import org.wicketstuff.select2.Select2Choice;
35

36
import java.io.IOException;
37
import java.io.Serializable;
38
import java.net.URISyntaxException;
39
import java.net.URLDecoder;
40
import java.net.URLEncoder;
41
import java.nio.charset.StandardCharsets;
42
import java.util.*;
43

44
/**
45
 * Utility class providing various helper methods for handling nanopublications, URIs, and other related functionalities.
46
 */
47
public class Utils {
48

49
    private Utils() {
50
    }  // no instances allowed
51

52
    public static final ValueFactory vf = SimpleValueFactory.getInstance();
2✔
53

54
    // TODO Merge with IriItem.getShortNameFromURI
55
    public static String getShortNameFromURI(IRI uri) {
56
        return getShortNameFromURI(uri.stringValue());
4✔
57
    }
58

59
    public static String getShortNameFromURI(String u) {
60
        u = u.replaceFirst("\\?.*$", "");
5✔
61
        u = u.replaceFirst("[/#]$", "");
5✔
62
        u = u.replaceFirst("^.*[/#]([^/#]*)[/#]([0-9]+)$", "$1/$2");
5✔
63
        u = u.replaceFirst("^.*[/#]([^/#]*[^0-9][^/#]*)$", "$1");
5✔
64
        u = u.replaceFirst("((^|[^A-Za-z0-9\\-_])RA[A-Za-z0-9\\-_]{8})[A-Za-z0-9\\-_]{35}$", "$1");
5✔
65
        u = u.replaceFirst("(^|[^A-Za-z0-9\\-_])RA[A-Za-z0-9\\-_]{43}[^A-Za-z0-9\\-_](.+)$", "$2");
5✔
66
        return u;
2✔
67
    }
68

69
    public static String getShortNanopubId(Object npId) {
70
        return TrustyUriUtils.getArtifactCode(npId.toString()).substring(0, 10);
7✔
71
    }
72

73
    private static Map<String, Nanopub> nanopubs = new HashMap<>();
4✔
74

75
    public static Nanopub getNanopub(String uriOrArtifactCode) {
76
        String artifactCode = getArtifactCode(uriOrArtifactCode);
3✔
77
        if (!nanopubs.containsKey(artifactCode)) {
4✔
78
            for (int i = 0; i < 3; i++) {  // Try 3 times to get nanopub
5!
79
                Nanopub np = GetNanopub.get(artifactCode);
3✔
80
                if (np != null) {
2!
81
                    nanopubs.put(artifactCode, np);
5✔
82
                    break;
1✔
83
                }
84
            }
85
        }
86
        return nanopubs.get(artifactCode);
5✔
87
    }
88

89
    public static String getArtifactCode(String uriOrArtifactCode) {
90
        return uriOrArtifactCode.replaceFirst("^.*(RA[0-9a-zA-Z\\-_]{43})(\\?.*)?$", "$1");
5✔
91
    }
92

93
    public static String urlEncode(Object o) {
94
        return URLEncoder.encode((o == null ? "" : o.toString()), Charsets.UTF_8);
9✔
95
    }
96

97
    public static String urlDecode(Object o) {
98
        return URLDecoder.decode((o == null ? "" : o.toString()), Charsets.UTF_8);
9✔
99
    }
100

101
    public static String getUrlWithParameters(String base, PageParameters parameters) {
102
        try {
103
            URIBuilder u = new URIBuilder(base);
5✔
104
            for (String key : parameters.getNamedKeys()) {
11✔
105
                for (StringValue value : parameters.getValues(key)) {
12✔
106
                    if (!value.isNull()) u.addParameter(key, value.toString());
9!
107
                }
1✔
108
            }
1✔
109
            return u.build().toString();
4✔
110
        } catch (URISyntaxException ex) {
1✔
111
            ex.printStackTrace();
2✔
112
            return "/";
2✔
113
        }
114
    }
115

116
    /**
117
     * Generates a short name for a public key or public key hash.
118
     *
119
     * @param pubkeyOrPubkeyhash the public key (64 characters) or public key hash (40 characters)
120
     * @return a short representation of the public key or public key hash
121
     */
122
    public static String getShortPubkeyName(String pubkeyOrPubkeyhash) {
123
        if (pubkeyOrPubkeyhash.length() == 64) {
4!
124
            return pubkeyOrPubkeyhash.replaceFirst("^(.{8}).*$", "$1");
×
125
        } else {
126
            return pubkeyOrPubkeyhash.replaceFirst("^(.).{39}(.{5}).*$", "$1..$2..");
5✔
127
        }
128
    }
129

130
    public static String getShortPubkeyhashLabel(String pubkeyOrPubkeyhash, IRI user) {
131
        String s = getShortPubkeyName(pubkeyOrPubkeyhash);
×
132
        NanodashSession session = NanodashSession.get();
×
133
        List<String> l = new ArrayList<>();
×
134
        if (pubkeyOrPubkeyhash.equals(session.getPubkeyString()) || pubkeyOrPubkeyhash.equals(session.getPubkeyhash()))
×
135
            l.add("local");
×
136
        // TODO: Make this more efficient:
137
        String hashed = Utils.createSha256HexHash(pubkeyOrPubkeyhash);
×
138
        if (User.getPubkeyhashes(user, true).contains(pubkeyOrPubkeyhash) || User.getPubkeyhashes(user, true).contains(hashed))
×
139
            l.add("approved");
×
140
        if (!l.isEmpty()) s += " (" + String.join("/", l) + ")";
×
141
        return s;
×
142
    }
143

144
    /**
145
     * Retrieves the name of the public key location based on the public key.
146
     *
147
     * @param pubkeyhash the public key string
148
     * @return the name of the public key location
149
     */
150
    public static String getPubkeyLocationName(String pubkeyhash) {
151
        return getPubkeyLocationName(pubkeyhash, getShortPubkeyName(pubkeyhash));
×
152
    }
153

154
    /**
155
     * Retrieves the name of the public key location, or returns a fallback name if not found.
156
     * If the key location is localhost, it returns "localhost".
157
     *
158
     * @param pubkeyhash the public key string
159
     * @param fallback   the fallback name to return if the key location is not found
160
     * @return the name of the public key location or the fallback name
161
     */
162
    public static String getPubkeyLocationName(String pubkeyhash, String fallback) {
163
        IRI keyLocation = User.getUserData().getKeyLocationForPubkeyhash(pubkeyhash);
×
164
        if (keyLocation == null) return fallback;
×
165
        if (keyLocation.stringValue().equals("http://localhost:37373/")) return "localhost";
×
166
        return keyLocation.stringValue().replaceFirst("https?://(nanobench\\.)?(nanodash\\.)?(.*[^/])/?$", "$3");
×
167
    }
168

169
    /**
170
     * Generates a short label for a public key location, including its status (local or approved).
171
     *
172
     * @param pubkeyhash the public key string
173
     * @param user       the IRI of the user associated with the public key
174
     * @return a short label indicating the public key location and its status
175
     */
176
    public static String getShortPubkeyLocationLabel(String pubkeyhash, IRI user) {
177
        String s = getPubkeyLocationName(pubkeyhash);
×
178
        NanodashSession session = NanodashSession.get();
×
179
        List<String> l = new ArrayList<>();
×
180
        if (pubkeyhash.equals(session.getPubkeyhash())) l.add("local");
×
181
        // TODO: Make this more efficient:
182
        if (User.getPubkeyhashes(user, true).contains(pubkeyhash)) l.add("approved");
×
183
        if (!l.isEmpty()) s += " (" + String.join("/", l) + ")";
×
184
        return s;
×
185
    }
186

187
    /**
188
     * Checks if a given public key has a Nanodash location.
189
     * A Nanodash location is identified by specific keywords in the key location.
190
     *
191
     * @param pubkeyhash the public key to check
192
     * @return true if the public key has a Nanodash location, false otherwise
193
     */
194
    public static boolean hasNanodashLocation(String pubkeyhash) {
195
        IRI keyLocation = User.getUserData().getKeyLocationForPubkeyhash(pubkeyhash);
×
196
        if (keyLocation == null) return true; // potentially a Nanodash location
×
197
        if (keyLocation.stringValue().contains("nanodash")) return true;
×
198
        if (keyLocation.stringValue().contains("nanobench")) return true;
×
199
        if (keyLocation.stringValue().contains(":37373")) return true;
×
200
        return false;
×
201
    }
202

203
    /**
204
     * Retrieves the short ORCID ID from an IRI object.
205
     *
206
     * @param orcidIri the IRI object representing the ORCID ID
207
     * @return the short ORCID ID as a string
208
     */
209
    public static String getShortOrcidId(IRI orcidIri) {
210
        return orcidIri.stringValue().replaceFirst("^https://orcid.org/", "");
6✔
211
    }
212

213
    /**
214
     * Retrieves the URI postfix from a given URI object.
215
     *
216
     * @param uri the URI object from which to extract the postfix
217
     * @return the URI postfix as a string
218
     */
219
    public static String getUriPostfix(Object uri) {
220
        String s = uri.toString();
3✔
221
        if (s.contains("#")) return s.replaceFirst("^.*#(.*)$", "$1");
4!
222
        return s.replaceFirst("^.*/(.*)$", "$1");
5✔
223
    }
224

225
    /**
226
     * Retrieves the URI prefix from a given URI object.
227
     *
228
     * @param uri the URI object from which to extract the prefix
229
     * @return the URI prefix as a string
230
     */
231
    public static String getUriPrefix(Object uri) {
232
        String s = uri.toString();
3✔
233
        if (s.contains("#")) return s.replaceFirst("^(.*#).*$", "$1");
4!
234
        return s.replaceFirst("^(.*/).*$", "$1");
5✔
235
    }
236

237
    /**
238
     * Checks if a given string is a valid URI postfix.
239
     * A valid URI postfix does not contain a colon (":").
240
     *
241
     * @param s the string to check
242
     * @return true if the string is a valid URI postfix, false otherwise
243
     */
244
    public static boolean isUriPostfix(String s) {
245
        return !s.contains(":");
8✔
246
    }
247

248
    /**
249
     * Retrieves the location of a given IntroNanopub.
250
     *
251
     * @param inp the IntroNanopub from which to extract the location
252
     * @return the IRI location of the nanopublication, or null if not found
253
     */
254
    public static IRI getLocation(IntroNanopub inp) {
255
        NanopubSignatureElement el = getNanopubSignatureElement(inp);
×
256
        for (KeyDeclaration kd : inp.getKeyDeclarations()) {
×
257
            if (el.getPublicKeyString().equals(kd.getPublicKeyString())) {
×
258
                return kd.getKeyLocation();
×
259
            }
260
        }
×
261
        return null;
×
262
    }
263

264
    /**
265
     * Retrieves the NanopubSignatureElement from a given IntroNanopub.
266
     *
267
     * @param inp the IntroNanopub from which to extract the signature element
268
     * @return the NanopubSignatureElement associated with the nanopublication
269
     */
270
    public static NanopubSignatureElement getNanopubSignatureElement(IntroNanopub inp) {
271
        try {
272
            return SignatureUtils.getSignatureElement(inp.getNanopub());
×
273
        } catch (MalformedCryptoElementException ex) {
×
274
            throw new RuntimeException(ex);
×
275
        }
276
    }
277

278
    /**
279
     * Retrieves a Nanopub object from a given URI if it is a potential Trusty URI.
280
     *
281
     * @param uri the URI to check and retrieve the Nanopub from
282
     * @return the Nanopub object if found, or null if not a known nanopublication
283
     */
284
    public static Nanopub getAsNanopub(String uri) {
285
        if (TrustyUriUtils.isPotentialTrustyUri(uri)) {
×
286
            try {
287
                return Utils.getNanopub(uri);
×
288
            } catch (Exception ex) {
×
289
                // wasn't a known nanopublication
290
            }
291
        }
292
        return null;
×
293
    }
294

295
    private static PolicyFactory htmlSanitizePolicy = new HtmlPolicyBuilder()
3✔
296
            .allowCommonBlockElements()
1✔
297
            .allowCommonInlineFormattingElements()
15✔
298
            .allowUrlProtocols("https", "http", "mailto")
7✔
299
            .allowElements("a")
7✔
300
            .allowAttributes("href").onElements("a")
14✔
301
            .allowElements("img")
7✔
302
            .allowAttributes("src").onElements("img")
8✔
303
            .requireRelNofollowOnLinks()
1✔
304
            .toFactory();
2✔
305

306
    /**
307
     * Sanitizes raw HTML input to ensure safe rendering.
308
     *
309
     * @param rawHtml the raw HTML input to sanitize
310
     * @return sanitized HTML string
311
     */
312
    public static String sanitizeHtml(String rawHtml) {
313
        return htmlSanitizePolicy.sanitize(rawHtml);
4✔
314
    }
315

316
    /**
317
     * Converts PageParameters to a URL-encoded string representation.
318
     *
319
     * @param params the PageParameters to convert
320
     * @return a string representation of the parameters in URL-encoded format
321
     */
322
    public static String getPageParametersAsString(PageParameters params) {
323
        String s = "";
2✔
324
        for (String n : params.getNamedKeys()) {
11✔
325
            if (!s.isEmpty()) s += "&";
6✔
326
            s += n + "=" + URLEncoder.encode(params.get(n).toString(), Charsets.UTF_8);
10✔
327
        }
1✔
328
        return s;
2✔
329
    }
330

331
    /**
332
     * Sets a minimal escape markup function for a Select2Choice component.
333
     * This function replaces certain characters and formats the display of choices.
334
     *
335
     * @param selectItem the Select2Choice component to set the escape markup for
336
     */
337
    public static void setSelect2ChoiceMinimalEscapeMarkup(Select2Choice<?> selectItem) {
338
        selectItem.getSettings().setEscapeMarkup("function(markup) {" +
×
339
                                                 "return markup" +
340
                                                 ".replaceAll('<','&lt;').replaceAll('>', '&gt;')" +
341
                                                 ".replace(/^(.*?) - /, '<span class=\"term\">$1</span><br>')" +
342
                                                 ".replace(/\\((https?:[\\S]+)\\)$/, '<br><code>$1</code>')" +
343
                                                 ".replace(/^([^<].*)$/, '<span class=\"term\">$1</span>')" +
344
                                                 ";}"
345
        );
346
    }
×
347

348
    /**
349
     * Checks if a nanopublication is of a specific class.
350
     *
351
     * @param np       the nanopublication to check
352
     * @param classIri the IRI of the class to check against
353
     * @return true if the nanopublication is of the specified class, false otherwise
354
     */
355
    public static boolean isNanopubOfClass(Nanopub np, IRI classIri) {
356
        return NanopubUtils.getTypes(np).contains(classIri);
5✔
357
    }
358

359
    /**
360
     * Checks if a nanopublication uses a specific predicate in its assertion.
361
     *
362
     * @param np           the nanopublication to check
363
     * @param predicateIri the IRI of the predicate to look for
364
     * @return true if the predicate is used in the assertion, false otherwise
365
     */
366
    public static boolean usesPredicateInAssertion(Nanopub np, IRI predicateIri) {
367
        for (Statement st : np.getAssertion()) {
11✔
368
            if (predicateIri.equals(st.getPredicate())) {
5✔
369
                return true;
2✔
370
            }
371
        }
1✔
372
        return false;
2✔
373
    }
374

375
    /**
376
     * Retrieves a map of FOAF names from the nanopublication's pubinfo.
377
     *
378
     * @param np the nanopublication from which to extract FOAF names
379
     * @return a map where keys are subjects and values are FOAF names
380
     */
381
    public static Map<String, String> getFoafNameMap(Nanopub np) {
382
        Map<String, String> foafNameMap = new HashMap<>();
4✔
383
        for (Statement st : np.getPubinfo()) {
11✔
384
            if (st.getPredicate().equals(FOAF.NAME) && st.getObject() instanceof Literal objL) {
14✔
385
                foafNameMap.put(st.getSubject().stringValue(), objL.stringValue());
8✔
386
            }
387
        }
1✔
388
        return foafNameMap;
2✔
389
    }
390

391
    /**
392
     * Creates an SHA-256 hash of the string representation of an object and returns it as a hexadecimal string.
393
     *
394
     * @param obj the object to hash
395
     * @return the SHA-256 hash of the object's string representation in hexadecimal format
396
     */
397
    public static String createSha256HexHash(Object obj) {
398
        return Hashing.sha256().hashString(obj.toString(), StandardCharsets.UTF_8).toString();
7✔
399
    }
400

401
    /**
402
     * Gets the types of a nanopublication.
403
     *
404
     * @param np the nanopublication from which to extract types
405
     * @return a list of IRI types associated with the nanopublication
406
     */
407
    public static List<IRI> getTypes(Nanopub np) {
408
        List<IRI> l = new ArrayList<IRI>();
4✔
409
        for (IRI t : NanopubUtils.getTypes(np)) {
11✔
410
            if (t.equals(FIP.AVAILABLE_FAIR_ENABLING_RESOURCE)) continue;
5✔
411
            if (t.equals(FIP.FAIR_ENABLING_RESOURCE_TO_BE_DEVELOPED))
4✔
412
                continue;
1✔
413
            if (t.equals(FIP.AVAILABLE_FAIR_SUPPORTING_RESOURCE)) continue;
4!
414
            if (t.equals(FIP.FAIR_SUPPORTING_RESOURCE_TO_BE_DEVELOPED))
4!
415
                continue;
×
416
            l.add(t);
4✔
417
        }
1✔
418
        return l;
2✔
419
    }
420

421
    /**
422
     * Gets a label for a type IRI.
423
     *
424
     * @param typeIri the IRI of the type
425
     * @return a label for the type, potentially truncated
426
     */
427
    public static String getTypeLabel(IRI typeIri) {
428
        if (typeIri.equals(FIP.FAIR_ENABLING_RESOURCE)) return "FER";
6✔
429
        if (typeIri.equals(FIP.FAIR_SUPPORTING_RESOURCE)) return "FSR";
6✔
430
        if (typeIri.equals(FIP.FAIR_IMPLEMENTATION_PROFILE)) return "FIP";
6✔
431
        if (typeIri.equals(NPX.DECLARED_BY)) return "user intro";
6✔
432
        String l = typeIri.stringValue();
3✔
433
        l = l.replaceFirst("^.*[/#]([^/#]+)[/#]?$", "$1");
5✔
434
        l = l.replaceFirst("^(.+)Nanopub$", "$1");
5✔
435
        if (l.length() > 25) l = l.substring(0, 20) + "...";
10✔
436
        return l;
2✔
437
    }
438

439
    /**
440
     * Gets a label for a URI.
441
     *
442
     * @param uri the URI to get the label from
443
     * @return a label for the URI, potentially truncated
444
     */
445
    public static String getUriLabel(String uri) {
446
        if (uri == null) return "";
4✔
447
        String uriLabel = uri;
2✔
448
        if (uriLabel.matches(".*[^A-Za-z0-9-_]RA[A-Za-z0-9-_]{43}([^A-Za-z0-9-_].*)?")) {
4✔
449
            String newUriLabel = uriLabel.replaceFirst("(.*[^A-Za-z0-9-_]RA[A-Za-z0-9-_]{8})[A-Za-z0-9-_]{35}([^A-Za-z0-9-_].*)?", "$1...$2");
5✔
450
            if (newUriLabel.length() <= 70) return newUriLabel;
6!
451
        }
452
        if (uriLabel.length() > 70) return uri.substring(0, 30) + "..." + uri.substring(uri.length() - 30);
16✔
453
        return uriLabel;
2✔
454
    }
455

456
    /**
457
     * Gets an ExternalLink with a URI label.
458
     *
459
     * @param markupId the markup ID for the link
460
     * @param uri      the URI to link to
461
     * @return an ExternalLink with the URI label
462
     */
463
    public static ExternalLink getUriLink(String markupId, String uri) {
464
        return new ExternalLink(markupId, (uri.startsWith("local:") ? "" : uri), getUriLabel(uri));
12!
465
    }
466

467
    /**
468
     * Gets an ExternalLink with a model for the URI label.
469
     *
470
     * @param markupId the markup ID for the link
471
     * @param model    the model containing the URI
472
     * @return an ExternalLink with the URI label
473
     */
474
    public static ExternalLink getUriLink(String markupId, IModel<String> model) {
475
        return new ExternalLink(markupId, model, new UriLabelModel(model));
×
476
    }
477

478
    private static class UriLabelModel implements IModel<String> {
479

480
        private static final long serialVersionUID = 1L;
481

482
        private IModel<String> uriModel;
483

484
        public UriLabelModel(IModel<String> uriModel) {
×
485
            this.uriModel = uriModel;
×
486
        }
×
487

488
        @Override
489
        public String getObject() {
490
            return getUriLabel(uriModel.getObject());
×
491
        }
492

493
    }
494

495
    /**
496
     * Creates a sublist from a list based on the specified indices.
497
     *
498
     * @param list      the list from which to create the sublist
499
     * @param fromIndex the starting index (inclusive) for the sublist
500
     * @param toIndex   the ending index (exclusive) for the sublist
501
     * @param <E>       the type of elements in the list
502
     * @return an ArrayList containing the elements from the specified range
503
     */
504
    public static <E> ArrayList<E> subList(List<E> list, long fromIndex, long toIndex) {
505
        // So the resulting list is serializable:
506
        return new ArrayList<E>(list.subList((int) fromIndex, (int) toIndex));
×
507
    }
508

509
    /**
510
     * Creates a sublist from an array based on the specified indices.
511
     *
512
     * @param array     the array from which to create the sublist
513
     * @param fromIndex the starting index (inclusive) for the sublist
514
     * @param toIndex   the ending index (exclusive) for the sublist
515
     * @param <E>       the type of elements in the array
516
     * @return an ArrayList containing the elements from the specified range
517
     */
518
    public static <E> ArrayList<E> subList(E[] array, long fromIndex, long toIndex) {
519
        return subList(Arrays.asList(array), fromIndex, toIndex);
×
520
    }
521

522
    /**
523
     * Comparator for sorting ApiResponseEntry objects based on a specified field.
524
     */
525
    // TODO Move this to ApiResponseEntry class?
526
    public static class ApiResponseEntrySorter implements Comparator<ApiResponseEntry>, Serializable {
527

528
        private static final long serialVersionUID = 1L;
529

530
        private String field;
531
        private boolean descending;
532

533
        /**
534
         * Constructor for ApiResponseEntrySorter.
535
         *
536
         * @param field      the field to sort by
537
         * @param descending if true, sorts in descending order; if false, sorts in ascending order
538
         */
539
        public ApiResponseEntrySorter(String field, boolean descending) {
×
540
            this.field = field;
×
541
            this.descending = descending;
×
542
        }
×
543

544
        /**
545
         * Compares two ApiResponseEntry objects based on the specified field.
546
         *
547
         * @param o1 the first object to be compared.
548
         * @param o2 the second object to be compared.
549
         * @return a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second.
550
         */
551
        @Override
552
        public int compare(ApiResponseEntry o1, ApiResponseEntry o2) {
553
            if (descending) {
×
554
                return o2.get(field).compareTo(o1.get(field));
×
555
            } else {
556
                return o1.get(field).compareTo(o2.get(field));
×
557
            }
558
        }
559

560
    }
561

562
    /**
563
     * MIME type for TriG RDF format.
564
     */
565
    public static final String TYPE_TRIG = "application/trig";
566

567
    /**
568
     * MIME type for Jelly RDF format.
569
     */
570
    public static final String TYPE_JELLY = "application/x-jelly-rdf";
571

572
    /**
573
     * MIME type for JSON-LD format.
574
     */
575
    public static final String TYPE_JSONLD = "application/ld+json";
576

577
    /**
578
     * MIME type for N-Quads format.
579
     */
580
    public static final String TYPE_NQUADS = "application/n-quads";
581

582
    /**
583
     * MIME type for Trix format.
584
     */
585
    public static final String TYPE_TRIX = "application/trix";
586

587
    /**
588
     * MIME type for HTML format.
589
     */
590
    public static final String TYPE_HTML = "text/html";
591

592
    public static final String SUPPORTED_TYPES =
593
            TYPE_TRIG + "," +
594
            TYPE_JELLY + "," +
595
            TYPE_JSONLD + "," +
596
            TYPE_NQUADS + "," +
597
            TYPE_TRIX + "," +
598
            TYPE_HTML;
599

600
    /**
601
     * List of supported MIME types for nanopublications.
602
     */
603
    public static final List<String> SUPPORTED_TYPES_LIST = Arrays.asList(StringUtils.split(SUPPORTED_TYPES, ','));
6✔
604

605
    // TODO Move these to nanopub-java library:
606

607
    /**
608
     * Retrieves a set of introduced IRI IDs from the nanopublication.
609
     *
610
     * @param np the nanopublication from which to extract introduced IRI IDs
611
     * @return a set of introduced IRI IDs
612
     */
613
    public static Set<String> getIntroducedIriIds(Nanopub np) {
614
        Set<String> introducedIriIds = new HashSet<>();
4✔
615
        for (Statement st : np.getPubinfo()) {
11✔
616
            if (!st.getSubject().equals(np.getUri())) continue;
7✔
617
            if (!st.getPredicate().equals(NPX.INTRODUCES)) continue;
6✔
618
            if (st.getObject() instanceof IRI obj) introducedIriIds.add(obj.stringValue());
14✔
619
        }
1✔
620
        return introducedIriIds;
2✔
621
    }
622

623
    /**
624
     * Retrieves a set of embedded IRI IDs from the nanopublication.
625
     *
626
     * @param np the nanopublication from which to extract embedded IRI IDs
627
     * @return a set of embedded IRI IDs
628
     */
629
    public static Set<String> getEmbeddedIriIds(Nanopub np) {
630
        Set<String> embeddedIriIds = new HashSet<>();
×
631
        for (Statement st : np.getPubinfo()) {
×
632
            if (!st.getSubject().equals(np.getUri())) continue;
×
633
            if (!st.getPredicate().equals(NPX.EMBEDS)) continue;
×
634
            if (st.getObject() instanceof IRI obj) embeddedIriIds.add(obj.stringValue());
×
635
        }
×
636
        return embeddedIriIds;
×
637
    }
638

639
    /**
640
     * Returns the URL of the default Nanopub Registry as configured by the given instance.
641
     *
642
     * @return Nanopub Registry URL
643
     */
644
    public static String getMainRegistryUrl() {
645
        try {
646
            return EnvironmentUtils.getProcEnvironment().getOrDefault("NANODASH_MAIN_REGISTRY", "https://registry.knowledgepixels.com/");
6✔
647
        } catch (IOException ex) {
×
648
            ex.printStackTrace();
×
649
            return "https://registry.knowledgepixels.com/";
×
650
        }
651
    }
652

653
    private static final String PLAIN_LITERAL_PATTERN = "^\"(([^\\\\\\\"]|\\\\\\\\|\\\\\")*)\"";
654
    private static final String LANGTAG_LITERAL_PATTERN = "^\"(([^\\\\\\\"]|\\\\\\\\|\\\\\")*)\"@([0-9a-zA-Z-]{2,})$";
655
    private static final String DATATYPE_LITERAL_PATTERN = "^\"(([^\\\\\\\"]|\\\\\\\\|\\\\\")*)\"\\^\\^<([^ ><\"^]+)>";
656

657
    /**
658
     * Checks whether string is valid literal serialization.
659
     *
660
     * @param literalString the literal string
661
     * @return true if valid
662
     */
663
    public static boolean isValidLiteralSerialization(String literalString) {
664
        if (literalString.matches(PLAIN_LITERAL_PATTERN)) {
×
665
            return true;
×
666
        } else if (literalString.matches(LANGTAG_LITERAL_PATTERN)) {
×
667
            return true;
×
668
        } else if (literalString.matches(DATATYPE_LITERAL_PATTERN)) {
×
669
            return true;
×
670
        }
671
        return false;
×
672
    }
673

674
    /**
675
     * Returns a serialized version of the literal.
676
     *
677
     * @param literal the literal
678
     * @return the String serialization of the literal
679
     */
680
    public static String getSerializedLiteral(Literal literal) {
681
        if (literal.getLanguage().isPresent()) {
×
682
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"@" + Literals.normalizeLanguageTag(literal.getLanguage().get());
×
683
        } else if (literal.getDatatype().equals(XSD.STRING)) {
×
684
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"";
×
685
        } else {
686
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"^^<" + literal.getDatatype() + ">";
×
687
        }
688
    }
689

690
    /**
691
     * Parses a serialized literal into a Literal object.
692
     *
693
     * @param serializedLiteral The serialized String of the literal
694
     * @return The parse Literal object
695
     */
696
    public static Literal getParsedLiteral(String serializedLiteral) {
697
        if (serializedLiteral.matches(PLAIN_LITERAL_PATTERN)) {
×
698
            return vf.createLiteral(getUnescapedLiteralString(serializedLiteral.replaceFirst(PLAIN_LITERAL_PATTERN, "$1")));
×
699
        } else if (serializedLiteral.matches(LANGTAG_LITERAL_PATTERN)) {
×
700
            String langtag = serializedLiteral.replaceFirst(LANGTAG_LITERAL_PATTERN, "$3");
×
701
            return vf.createLiteral(getUnescapedLiteralString(serializedLiteral.replaceFirst(LANGTAG_LITERAL_PATTERN, "$1")), langtag);
×
702
        } else if (serializedLiteral.matches(DATATYPE_LITERAL_PATTERN)) {
×
703
            IRI datatype = vf.createIRI(serializedLiteral.replaceFirst(DATATYPE_LITERAL_PATTERN, "$3"));
×
704
            return vf.createLiteral(getUnescapedLiteralString(serializedLiteral.replaceFirst(DATATYPE_LITERAL_PATTERN, "$1")), datatype);
×
705
        }
706
        throw new IllegalArgumentException("Not a valid literal serialization: " + serializedLiteral);
×
707
    }
708

709
    /**
710
     * Escapes quotes (") and slashes (/) of a literal string.
711
     *
712
     * @param unescapedString un-escaped string
713
     * @return escaped string
714
     */
715
    public static String getEscapedLiteralString(String unescapedString) {
716
        return unescapedString.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\"");
×
717
    }
718

719
    /**
720
     * Un-escapes quotes (") and slashes (/) of a literal string.
721
     *
722
     * @param escapedString escaped string
723
     * @return un-escaped string
724
     */
725
    public static String getUnescapedLiteralString(String escapedString) {
726
        return escapedString.replaceAll("\\\\(\\\\|\\\")", "$1");
×
727
    }
728

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