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

knowledgepixels / nanodash / 17265760757

27 Aug 2025 11:45AM UTC coverage: 12.362% (-0.04%) from 12.401%
17265760757

push

github

tkuhn
Some minor refactoring and improvements of literal handling

332 of 3834 branches covered (8.66%)

Branch coverage included in aggregate %.

989 of 6852 relevant lines covered (14.43%)

0.64 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 java.io.IOException;
4
import java.io.Serializable;
5
import java.net.URISyntaxException;
6
import java.net.URLDecoder;
7
import java.net.URLEncoder;
8
import java.nio.charset.StandardCharsets;
9
import java.util.ArrayList;
10
import java.util.Arrays;
11
import java.util.Comparator;
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Map;
16
import java.util.Set;
17

18
import org.apache.commons.codec.Charsets;
19
import org.apache.commons.exec.environment.EnvironmentUtils;
20
import org.apache.commons.lang.StringUtils;
21
import org.apache.http.client.utils.URIBuilder;
22
import org.apache.wicket.markup.html.link.ExternalLink;
23
import org.apache.wicket.model.IModel;
24
import org.apache.wicket.request.mapper.parameter.PageParameters;
25
import org.apache.wicket.util.string.StringValue;
26
import org.eclipse.rdf4j.model.IRI;
27
import org.eclipse.rdf4j.model.Literal;
28
import org.eclipse.rdf4j.model.Statement;
29
import org.eclipse.rdf4j.model.ValueFactory;
30
import org.eclipse.rdf4j.model.base.CoreDatatype.XSD;
31
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
32
import org.eclipse.rdf4j.model.util.Literals;
33
import org.eclipse.rdf4j.model.vocabulary.FOAF;
34
import org.nanopub.Nanopub;
35
import org.nanopub.NanopubUtils;
36
import org.nanopub.extra.security.KeyDeclaration;
37
import org.nanopub.extra.security.MalformedCryptoElementException;
38
import org.nanopub.extra.security.NanopubSignatureElement;
39
import org.nanopub.extra.security.SignatureUtils;
40
import org.nanopub.extra.server.GetNanopub;
41
import org.nanopub.extra.services.ApiResponseEntry;
42
import org.nanopub.extra.setting.IntroNanopub;
43
import org.owasp.html.HtmlPolicyBuilder;
44
import org.owasp.html.PolicyFactory;
45
import org.wicketstuff.select2.Select2Choice;
46

47
import com.google.common.hash.Hashing;
48

49
import net.trustyuri.TrustyUriUtils;
50

51
/**
52
 * Utility class providing various helper methods for handling nanopublications, URIs, and other related functionalities.
53
 */
54
public class Utils {
55

56
    private Utils() {
57
    }  // no instances allowed
58

59
    public static final ValueFactory vf = SimpleValueFactory.getInstance();
2✔
60

61
    // TODO Merge with IriItem.getShortNameFromURI
62
    public static String getShortNameFromURI(IRI uri) {
63
        return getShortNameFromURI(uri.stringValue());
4✔
64
    }
65

66
    public static String getShortNameFromURI(String u) {
67
        u = u.replaceFirst("\\?.*$", "");
5✔
68
        u = u.replaceFirst("[/#]$", "");
5✔
69
        u = u.replaceFirst("^.*[/#]([^/#]*)[/#]([0-9]+)$", "$1/$2");
5✔
70
        u = u.replaceFirst("^.*[/#]([^/#]*[^0-9][^/#]*)$", "$1");
5✔
71
        u = u.replaceFirst("((^|[^A-Za-z0-9\\-_])RA[A-Za-z0-9\\-_]{8})[A-Za-z0-9\\-_]{35}$", "$1");
5✔
72
        u = u.replaceFirst("(^|[^A-Za-z0-9\\-_])RA[A-Za-z0-9\\-_]{43}[^A-Za-z0-9\\-_](.+)$", "$2");
5✔
73
        return u;
2✔
74
    }
75

76
    public static String getShortNanopubId(Object npId) {
77
        return TrustyUriUtils.getArtifactCode(npId.toString()).substring(0, 10);
7✔
78
    }
79

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

82
    public static Nanopub getNanopub(String uriOrArtifactCode) {
83
        String artifactCode = getArtifactCode(uriOrArtifactCode);
3✔
84
        if (!nanopubs.containsKey(artifactCode)) {
4✔
85
            for (int i = 0; i < 3; i++) {  // Try 3 times to get nanopub
5!
86
                Nanopub np = GetNanopub.get(artifactCode);
3✔
87
                if (np != null) {
2!
88
                    nanopubs.put(artifactCode, np);
5✔
89
                    break;
1✔
90
                }
91
            }
92
        }
93
        return nanopubs.get(artifactCode);
5✔
94
    }
95

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

100
    public static String urlEncode(Object o) {
101
        return URLEncoder.encode((o == null ? "" : o.toString()), Charsets.UTF_8);
9✔
102
    }
103

104
    public static String urlDecode(Object o) {
105
        return URLDecoder.decode((o == null ? "" : o.toString()), Charsets.UTF_8);
9✔
106
    }
107

108
    public static String getUrlWithParameters(String base, PageParameters parameters) {
109
        try {
110
            URIBuilder u = new URIBuilder(base);
5✔
111
            for (String key : parameters.getNamedKeys()) {
11✔
112
                for (StringValue value : parameters.getValues(key)) {
12✔
113
                    if (!value.isNull()) u.addParameter(key, value.toString());
9!
114
                }
1✔
115
            }
1✔
116
            return u.build().toString();
4✔
117
        } catch (URISyntaxException ex) {
1✔
118
            ex.printStackTrace();
2✔
119
            return "/";
2✔
120
        }
121
    }
122

123
    /**
124
     * Generates a short name for a public key or public key hash.
125
     *
126
     * @param pubkeyOrPubkeyhash the public key (64 characters) or public key hash (40 characters)
127
     * @return a short representation of the public key or public key hash
128
     */
129
    public static String getShortPubkeyName(String pubkeyOrPubkeyhash) {
130
        if (pubkeyOrPubkeyhash.length() == 64) {
4!
131
            return pubkeyOrPubkeyhash.replaceFirst("^(.{8}).*$", "$1");
×
132
        } else {
133
            return pubkeyOrPubkeyhash.replaceFirst("^(.).{39}(.{5}).*$", "$1..$2..");
5✔
134
        }
135
    }
136

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

151
    /**
152
     * Retrieves the name of the public key location based on the public key.
153
     *
154
     * @param pubkeyhash the public key string
155
     * @return the name of the public key location
156
     */
157
    public static String getPubkeyLocationName(String pubkeyhash) {
158
        return getPubkeyLocationName(pubkeyhash, getShortPubkeyName(pubkeyhash));
×
159
    }
160

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

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

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

210
    /**
211
     * Retrieves the short ORCID ID from an IRI object.
212
     *
213
     * @param orcidIri the IRI object representing the ORCID ID
214
     * @return the short ORCID ID as a string
215
     */
216
    public static String getShortOrcidId(IRI orcidIri) {
217
        return orcidIri.stringValue().replaceFirst("^https://orcid.org/", "");
6✔
218
    }
219

220
    /**
221
     * Retrieves the URI postfix from a given URI object.
222
     *
223
     * @param uri the URI object from which to extract the postfix
224
     * @return the URI postfix as a string
225
     */
226
    public static String getUriPostfix(Object uri) {
227
        String s = uri.toString();
3✔
228
        if (s.contains("#")) return s.replaceFirst("^.*#(.*)$", "$1");
4!
229
        return s.replaceFirst("^.*/(.*)$", "$1");
5✔
230
    }
231

232
    /**
233
     * Retrieves the URI prefix from a given URI object.
234
     *
235
     * @param uri the URI object from which to extract the prefix
236
     * @return the URI prefix as a string
237
     */
238
    public static String getUriPrefix(Object uri) {
239
        String s = uri.toString();
3✔
240
        if (s.contains("#")) return s.replaceFirst("^(.*#).*$", "$1");
4!
241
        return s.replaceFirst("^(.*/).*$", "$1");
5✔
242
    }
243

244
    /**
245
     * Checks if a given string is a valid URI postfix.
246
     * A valid URI postfix does not contain a colon (":").
247
     *
248
     * @param s the string to check
249
     * @return true if the string is a valid URI postfix, false otherwise
250
     */
251
    public static boolean isUriPostfix(String s) {
252
        return !s.contains(":");
8✔
253
    }
254

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

271
    /**
272
     * Retrieves the NanopubSignatureElement from a given IntroNanopub.
273
     *
274
     * @param inp the IntroNanopub from which to extract the signature element
275
     * @return the NanopubSignatureElement associated with the nanopublication
276
     */
277
    public static NanopubSignatureElement getNanopubSignatureElement(IntroNanopub inp) {
278
        try {
279
            return SignatureUtils.getSignatureElement(inp.getNanopub());
×
280
        } catch (MalformedCryptoElementException ex) {
×
281
            throw new RuntimeException(ex);
×
282
        }
283
    }
284

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

302
    private static PolicyFactory htmlSanitizePolicy = new HtmlPolicyBuilder()
3✔
303
            .allowCommonBlockElements()
1✔
304
            .allowCommonInlineFormattingElements()
15✔
305
            .allowUrlProtocols("https", "http", "mailto")
7✔
306
            .allowElements("a")
7✔
307
            .allowAttributes("href").onElements("a")
14✔
308
            .allowElements("img")
7✔
309
            .allowAttributes("src").onElements("img")
8✔
310
            .requireRelNofollowOnLinks()
1✔
311
            .toFactory();
2✔
312

313
    /**
314
     * Sanitizes raw HTML input to ensure safe rendering.
315
     *
316
     * @param rawHtml the raw HTML input to sanitize
317
     * @return sanitized HTML string
318
     */
319
    public static String sanitizeHtml(String rawHtml) {
320
        return htmlSanitizePolicy.sanitize(rawHtml);
4✔
321
    }
322

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

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

355
    /**
356
     * Checks if a nanopublication is of a specific class.
357
     *
358
     * @param np       the nanopublication to check
359
     * @param classIri the IRI of the class to check against
360
     * @return true if the nanopublication is of the specified class, false otherwise
361
     */
362
    public static boolean isNanopubOfClass(Nanopub np, IRI classIri) {
363
        return NanopubUtils.getTypes(np).contains(classIri);
5✔
364
    }
365

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

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

398
    /**
399
     * Creates an SHA-256 hash of the string representation of an object and returns it as a hexadecimal string.
400
     *
401
     * @param obj the object to hash
402
     * @return the SHA-256 hash of the object's string representation in hexadecimal format
403
     */
404
    public static String createSha256HexHash(Object obj) {
405
        return Hashing.sha256().hashString(obj.toString(), StandardCharsets.UTF_8).toString();
7✔
406
    }
407

408
    /**
409
     * Gets the types of a nanopublication.
410
     *
411
     * @param np the nanopublication from which to extract types
412
     * @return a list of IRI types associated with the nanopublication
413
     */
414
    public static List<IRI> getTypes(Nanopub np) {
415
        List<IRI> l = new ArrayList<IRI>();
4✔
416
        for (IRI t : NanopubUtils.getTypes(np)) {
11✔
417
            if (t.stringValue().equals("https://w3id.org/fair/fip/terms/Available-FAIR-Enabling-Resource")) continue;
6✔
418
            if (t.stringValue().equals("https://w3id.org/fair/fip/terms/FAIR-Enabling-Resource-to-be-Developed"))
5✔
419
                continue;
1✔
420
            if (t.stringValue().equals("https://w3id.org/fair/fip/terms/Available-FAIR-Supporting-Resource")) continue;
5!
421
            if (t.stringValue().equals("https://w3id.org/fair/fip/terms/FAIR-Supporting-Resource-to-be-Developed"))
5!
422
                continue;
×
423
            l.add(t);
4✔
424
        }
1✔
425
        return l;
2✔
426
    }
427

428
    /**
429
     * Gets a label for a type IRI.
430
     *
431
     * @param typeIri the IRI of the type
432
     * @return a label for the type, potentially truncated
433
     */
434
    public static String getTypeLabel(IRI typeIri) {
435
        String l = typeIri.stringValue();
3✔
436
        if (l.equals("https://w3id.org/fair/fip/terms/FAIR-Enabling-Resource")) return "FER";
6✔
437
        if (l.equals("https://w3id.org/fair/fip/terms/FAIR-Supporting-Resource")) return "FSR";
6✔
438
        if (l.equals("https://w3id.org/fair/fip/terms/FAIR-Implementation-Profile")) return "FIP";
6✔
439
        if (l.equals("http://purl.org/nanopub/x/declaredBy")) return "user intro";
6✔
440
        l = l.replaceFirst("^.*[/#]([^/#]+)[/#]?$", "$1");
5✔
441
        l = l.replaceFirst("^(.+)Nanopub$", "$1");
5✔
442
        if (l.length() > 25) l = l.substring(0, 20) + "...";
10✔
443
        return l;
2✔
444
    }
445

446
    /**
447
     * Gets a label for a URI.
448
     *
449
     * @param uri the URI to get the label from
450
     * @return a label for the URI, potentially truncated
451
     */
452
    public static String getUriLabel(String uri) {
453
        if (uri == null) return "";
4✔
454
        String uriLabel = uri;
2✔
455
        if (uriLabel.matches(".*[^A-Za-z0-9-_]RA[A-Za-z0-9-_]{43}([^A-Za-z0-9-_].*)?")) {
4✔
456
            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✔
457
            if (newUriLabel.length() <= 70) return newUriLabel;
6!
458
        }
459
        if (uriLabel.length() > 70) return uri.substring(0, 30) + "..." + uri.substring(uri.length() - 30);
16✔
460
        return uriLabel;
2✔
461
    }
462

463
    /**
464
     * Gets an ExternalLink with a URI label.
465
     *
466
     * @param markupId the markup ID for the link
467
     * @param uri      the URI to link to
468
     * @return an ExternalLink with the URI label
469
     */
470
    public static ExternalLink getUriLink(String markupId, String uri) {
471
        return new ExternalLink(markupId, (uri.startsWith("local:") ? "" : uri), getUriLabel(uri));
12!
472
    }
473

474
    /**
475
     * Gets an ExternalLink with a model for the URI label.
476
     *
477
     * @param markupId the markup ID for the link
478
     * @param model    the model containing the URI
479
     * @return an ExternalLink with the URI label
480
     */
481
    public static ExternalLink getUriLink(String markupId, IModel<String> model) {
482
        return new ExternalLink(markupId, model, new UriLabelModel(model));
×
483
    }
484

485
    private static class UriLabelModel implements IModel<String> {
486

487
        private static final long serialVersionUID = 1L;
488

489
        private IModel<String> uriModel;
490

491
        public UriLabelModel(IModel<String> uriModel) {
×
492
            this.uriModel = uriModel;
×
493
        }
×
494

495
        @Override
496
        public String getObject() {
497
            return getUriLabel(uriModel.getObject());
×
498
        }
499

500
    }
501

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

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

529
    /**
530
     * Comparator for sorting ApiResponseEntry objects based on a specified field.
531
     */
532
    // TODO Move this to ApiResponseEntry class?
533
    public static class ApiResponseEntrySorter implements Comparator<ApiResponseEntry>, Serializable {
534

535
        private static final long serialVersionUID = 1L;
536

537
        private String field;
538
        private boolean descending;
539

540
        /**
541
         * Constructor for ApiResponseEntrySorter.
542
         *
543
         * @param field      the field to sort by
544
         * @param descending if true, sorts in descending order; if false, sorts in ascending order
545
         */
546
        public ApiResponseEntrySorter(String field, boolean descending) {
×
547
            this.field = field;
×
548
            this.descending = descending;
×
549
        }
×
550

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

567
    }
568

569
    /**
570
     * MIME type for TriG RDF format.
571
     */
572
    public static final String TYPE_TRIG = "application/trig";
573

574
    /**
575
     * MIME type for Jelly RDF format.
576
     */
577
    public static final String TYPE_JELLY = "application/x-jelly-rdf";
578

579
    /**
580
     * MIME type for JSON-LD format.
581
     */
582
    public static final String TYPE_JSONLD = "application/ld+json";
583

584
    /**
585
     * MIME type for N-Quads format.
586
     */
587
    public static final String TYPE_NQUADS = "application/n-quads";
588

589
    /**
590
     * MIME type for Trix format.
591
     */
592
    public static final String TYPE_TRIX = "application/trix";
593

594
    /**
595
     * MIME type for HTML format.
596
     */
597
    public static final String TYPE_HTML = "text/html";
598

599
    public static final String SUPPORTED_TYPES =
600
            TYPE_TRIG + "," +
601
            TYPE_JELLY + "," +
602
            TYPE_JSONLD + "," +
603
            TYPE_NQUADS + "," +
604
            TYPE_TRIX + "," +
605
            TYPE_HTML;
606

607
    /**
608
     * List of supported MIME types for nanopublications.
609
     */
610
    public static final List<String> SUPPORTED_TYPES_LIST = Arrays.asList(StringUtils.split(SUPPORTED_TYPES, ','));
6✔
611

612
    // TODO Move these to nanopub-java library:
613

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

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

646
    /**
647
     * Returns the URL of the default Nanopub Registry as configured by the given instance.
648
     *
649
     * @return Nanopub Registry URL
650
     */
651
    public static String getMainRegistryUrl() {
652
        try {
653
            return EnvironmentUtils.getProcEnvironment().getOrDefault("NANODASH_MAIN_REGISTRY", "https://registry.knowledgepixels.com/");
6✔
654
        } catch (IOException ex) {
×
655
            ex.printStackTrace();
×
656
            return "https://registry.knowledgepixels.com/";
×
657
        }
658
    }
659

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

664
    /**
665
     * Checks whether string is valid literal serialization.
666
     *
667
     * @param literalString the literal string
668
     * @return true if valid
669
     */
670
    public static boolean isValidLiteralSerialization(String literalString) {
671
        if (literalString.matches(PLAIN_LITERAL_PATTERN)) {
×
672
            return true;
×
673
        } else if (literalString.matches(LANGTAG_LITERAL_PATTERN)) {
×
674
            return true;
×
675
        } else if (literalString.matches(DATATYPE_LITERAL_PATTERN)) {
×
676
            return true;
×
677
        }
678
        return false;
×
679
    }
680

681
    /**
682
     * Returns a serialized version of the literal.
683
     *
684
     * @param literal the literal
685
     * @return the String serialization of the literal
686
     */
687
    public static String getSerializedLiteral(Literal literal) {
688
        if (literal.getLanguage().isPresent()) {
×
689
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"@" + Literals.normalizeLanguageTag(literal.getLanguage().get());
×
690
        } else if (literal.getDatatype().equals(XSD.STRING)) {
×
691
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"";
×
692
        } else {
693
            return "\"" + getEscapedLiteralString(literal.stringValue()) + "\"^^<" + literal.getDatatype() + ">";
×
694
        }
695
    }
696

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

716
    /**
717
     * Escapes quotes (") and slashes (/) of a literal string.
718
     *
719
     * @param unescapedString un-escaped string
720
     * @return escaped string
721
     */
722
    public static String getEscapedLiteralString(String unescapedString) {
723
        return unescapedString.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\"");
×
724
    }
725

726
    /**
727
     * Un-escapes quotes (") and slashes (/) of a literal string.
728
     *
729
     * @param unescapedString escaped string
730
     * @return un-escaped string
731
     */
732
    public static String getUnescapedLiteralString(String escapedString) {
733
        return escapedString.replaceAll("\\\\(\\\\|\\\")", "$1");
×
734
    }
735

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