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

knowledgepixels / nanodash / 17837235071

18 Sep 2025 05:58PM UTC coverage: 13.87%. Remained the same
17837235071

push

github

tkuhn
chore: Remove serialVersionUIDs

443 of 4022 branches covered (11.01%)

Branch coverage included in aggregate %.

1133 of 7341 relevant lines covered (15.43%)

0.68 hits per line

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

67.82
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.impl.SimpleValueFactory;
18
import org.eclipse.rdf4j.model.util.Literals;
19
import org.eclipse.rdf4j.model.vocabulary.FOAF;
20
import org.eclipse.rdf4j.model.vocabulary.XSD;
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.slf4j.Logger;
35
import org.slf4j.LoggerFactory;
36
import org.wicketstuff.select2.Select2Choice;
37

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

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

51
    private Utils() {
52
    }  // no instances allowed
53

54
    public static final ValueFactory vf = SimpleValueFactory.getInstance();
2✔
55
    private static final Logger logger = LoggerFactory.getLogger(Utils.class);
3✔
56

57
    // TODO Merge with IriItem.getShortNameFromURI
58
    public static String getShortNameFromURI(IRI uri) {
59
        return getShortNameFromURI(uri.stringValue());
4✔
60
    }
61

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

72
    public static String getShortNanopubId(Object npId) {
73
        return TrustyUriUtils.getArtifactCode(npId.toString()).substring(0, 10);
7✔
74
    }
75

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

459
    /**
460
     * Gets an ExternalLink with a URI label.
461
     *
462
     * @param markupId the markup ID for the link
463
     * @param uri      the URI to link to
464
     * @return an ExternalLink with the URI label
465
     */
466
    public static ExternalLink getUriLink(String markupId, String uri) {
467
        return new ExternalLink(markupId, (Utils.isLocalURI(uri) ? "" : uri), getUriLabel(uri));
13✔
468
    }
469

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

481
    private static class UriLabelModel implements IModel<String> {
482

483
        private IModel<String> uriModel;
484

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

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

494
    }
495

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

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

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

529
        private String field;
530
        private boolean descending;
531

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

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

559
    }
560

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

728
    /**
729
     * Checks if a given IRI is a local URI.
730
     *
731
     * @param uri the IRI to check
732
     * @return true if the IRI is a local URI, false otherwise
733
     */
734
    public static boolean isLocalURI(IRI uri) {
735
        return uri != null && isLocalURI(uri.stringValue());
×
736
    }
737

738
    /**
739
     * Checks if a given string is a local URI.
740
     *
741
     * @param uriAsString the string to check
742
     * @return true if the string is a local URI, false otherwise
743
     */
744
    public static boolean isLocalURI(String uriAsString) {
745
        return !uriAsString.isBlank() && uriAsString.startsWith(LocalUri.PREFIX);
11!
746
    }
747

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