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

knowledgepixels / nanodash / 23531203576

25 Mar 2026 08:10AM UTC coverage: 16.367% (-0.02%) from 16.386%
23531203576

push

github

tkuhn
fix: sort named users before ID-only users in space user lists

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

751 of 5651 branches covered (13.29%)

Branch coverage included in aggregate %.

1900 of 10546 relevant lines covered (18.02%)

2.47 hits per line

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

38.67
src/main/java/com/knowledgepixels/nanodash/domain/UserData.java
1
package com.knowledgepixels.nanodash.domain;
2

3
import com.knowledgepixels.nanodash.*;
4
import org.eclipse.rdf4j.common.exception.RDF4JException;
5
import org.eclipse.rdf4j.model.IRI;
6
import org.eclipse.rdf4j.model.ValueFactory;
7
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
8
import org.eclipse.rdf4j.model.util.Values;
9
import org.nanopub.MalformedNanopubException;
10
import org.nanopub.Nanopub;
11
import org.nanopub.SimpleTimestampPattern;
12
import org.nanopub.extra.security.MalformedCryptoElementException;
13
import org.nanopub.extra.security.NanopubSignatureElement;
14
import org.nanopub.extra.security.SignatureUtils;
15
import org.nanopub.extra.server.GetNanopub;
16
import org.nanopub.extra.services.ApiResponseEntry;
17
import org.nanopub.extra.services.QueryRef;
18
import org.nanopub.extra.setting.IntroNanopub;
19
import org.nanopub.extra.setting.NanopubSetting;
20
import org.nanopub.vocabulary.NPX;
21
import org.slf4j.Logger;
22
import org.slf4j.LoggerFactory;
23

24
import java.io.IOException;
25
import java.io.Serializable;
26
import java.util.*;
27

28
/**
29
 * UserData class manages user-related data.
30
 */
31
public class UserData implements Serializable {
32

33
    private static ValueFactory vf = SimpleValueFactory.getInstance();
6✔
34
    private static final Logger logger = LoggerFactory.getLogger(UserData.class);
12✔
35

36
    private HashMap<IRI, Set<String>> approvedIdPubkeyhashMap = new HashMap<>();
15✔
37
    private HashMap<String, Set<IRI>> approvedPubkeyhashIdMap = new HashMap<>();
15✔
38
    private HashMap<String, Set<IRI>> approvedPubkeyhashLocationMap = new HashMap<>();
15✔
39
    private HashMap<IRI, Set<String>> unapprovedIdPubkeyhashMap = new HashMap<>();
15✔
40
    private HashMap<String, Set<IRI>> unapprovedPubkeyhashIdMap = new HashMap<>();
15✔
41
    private HashMap<String, Set<IRI>> unapprovedPubkeyhashLocationMap = new HashMap<>();
15✔
42
    private HashMap<IRI, Boolean> softwareIdMap = new HashMap<>();
15✔
43
    private HashMap<String, Set<IRI>> pubkeyhashIntroMap = new HashMap<>();
15✔
44
    private HashMap<IRI, IntroNanopub> introMap = new HashMap<>();
15✔
45
    private Set<IRI> approvedIntros = new HashSet<>();
15✔
46
    private HashMap<IRI, String> idNameMap = new HashMap<>();
15✔
47
    private HashMap<IRI, List<IntroNanopub>> introNanopubLists = new HashMap<>();
15✔
48
    private final HashMap<IRI, IRI> profilePictures = new HashMap<>();
15✔
49
    private final HashMap<IRI, IRI> defaultLicense = new HashMap<>();
15✔
50

51
    /**
52
     * Default constructor for UserData.
53
     * Initializes the user data by fetching nanopublications settings.
54
     */
55
    UserData() {
6✔
56
        final NanodashPreferences pref = NanodashPreferences.get();
6✔
57

58
        // TODO Make nanopublication setting configurable:
59
        NanopubSetting setting;
60
        if (pref.getSettingUri() != null) {
9!
61
            setting = new NanopubSetting(GetNanopub.get(pref.getSettingUri()));
×
62
        } else {
63
            try {
64
                setting = NanopubSetting.getLocalSetting();
6✔
65
            } catch (RDF4JException | MalformedNanopubException | IOException ex) {
×
66
                throw new RuntimeException(ex);
×
67
            }
3✔
68
        }
69
        String settingId = setting.getNanopub().getUri().stringValue();
15✔
70
        if (setting.getUpdateStrategy().equals(NPX.UPDATES_BY_CREATOR)) {
15!
71
            settingId = QueryApiAccess.getLatestVersionId(settingId);
9✔
72
            setting = new NanopubSetting(GetNanopub.get(settingId));
18✔
73
        }
74
        logger.info("Using nanopublication setting: {}", settingId);
12✔
75

76
//                // Get users that are listed directly in the authority index, and consider them approved:
77
//                ByteArrayOutputStream out = new ByteArrayOutputStream(); // TODO use piped out-in stream here
78
//                new FetchIndex(setting.getAgentIntroCollection().stringValue(), out, RDFFormat.TRIG, false, true, null).run();
79
//                InputStream in = new ByteArrayInputStream(out.toByteArray());
80
//                try {
81
//                        MultiNanopubRdfHandler.process(RDFFormat.TRIG, in, new MultiNanopubRdfHandler.NanopubHandler() {
82
//                                @Override
83
//                                public void handleNanopub(Nanopub np) {
84
//                                        // TODO: Check that latest version talks about same user
85
//                                        register(QueryApiAccess.getLatestVersionId(np.getUri().stringValue()), true);
86
//                                }
87
//                        });
88
//                } catch (RDFParseException | RDFHandlerException | IOException | MalformedNanopubException ex) {
89
//                        logger.error();
90
//                }
91
///
92
//                if (setting.getTrustRangeAlgorithm().equals(NPX.TRANSITIVE_TRUST)) {
93
//                        ApiResponse resp = QueryApiAccess.forcedGet("get-approved-nanopubs");
94
//                        List<ApiResponseEntry> results = new ArrayList<>(resp.getData());
95
//                        while (true) {
96
//                                boolean keepLooping = false;
97
//                                for (ApiResponseEntry entry : new ArrayList<>(results)) {
98
//                                        if (hasValue(approvedPubkeyIdMap, entry.get("pubkey"), Utils.vf.createIRI(entry.get("approver")))) {
99
//                                                register(entry.get("approved_np"), true);
100
//                                                results.remove(entry);
101
//                                                keepLooping = true;
102
//                                        }
103
//                                }
104
//                                if (!keepLooping) break;
105
//                        }
106
//                }
107

108
        logger.info("Loading approved users...");
9✔
109
        try {
110
            for (RegistryAccountInfo rai : RegistryAccountInfo.fromUrl(Utils.getMainRegistryUrl() + "list.json")) {
36✔
111
                registerApproved(rai);
9✔
112
            }
3✔
113
        } catch (Exception ex) {
×
114
            throw new RuntimeException(ex);
×
115
        }
3✔
116

117
        logger.info("Loading user details...");
9✔
118
        // Get latest introductions for all users, including unapproved ones:
119
        for (ApiResponseEntry entry : ApiCache.retrieveResponseSync(new QueryRef(QueryApiAccess.GET_ALL_USER_INTROS), true).getData()) {
48✔
120
            register(entry);
9✔
121
        }
3✔
122

123
        for (ApiResponseEntry entry : ApiCache.retrieveResponseSync(new QueryRef(QueryApiAccess.GET_ALL_USER_PROFILE_PICS), true).getData()) {
48✔
124
            profilePictures.put(Values.iri(entry.get("user")), Values.iri(entry.get("imageUrl")));
36✔
125
        }
3✔
126

127
        for (ApiResponseEntry entry : ApiCache.retrieveResponseSync(new QueryRef(QueryApiAccess.GET_ALL_USER_DEFAULT_LICENSE), true).getData()) {
48✔
128
            defaultLicense.put(Values.iri(entry.get("user")), Values.iri(entry.get("license")));
36✔
129
        }
3✔
130
    }
3✔
131

132
    private IntroNanopub toIntroNanopub(IRI iri) {
133
        if (iri == null) return null;
×
134
        if (introMap.containsKey(iri)) return introMap.get(iri);
×
135
        Nanopub np = Utils.getNanopub(iri.stringValue());
×
136
        if (np == null) return null;
×
137
        IntroNanopub introNp = new IntroNanopub(np);
×
138
        introMap.put(np.getUri(), introNp);
×
139
        return introNp;
×
140
    }
141

142
    private void registerApproved(RegistryAccountInfo rai) {
143
        if (rai.getAgent().equals("$")) return;
18✔
144
        addValue(approvedIdPubkeyhashMap, rai.getAgentIri(), rai.getPubkey());
24✔
145
        addValue(approvedPubkeyhashIdMap, rai.getPubkey(), rai.getAgentIri());
24✔
146
    }
3✔
147

148
    private void register(ApiResponseEntry entry) {
149
        IRI userIri;
150
        try {
151
            userIri = vf.createIRI(entry.get("user"));
18✔
152
        } catch (IllegalArgumentException ex) {
×
153
            logger.error("Error creating IRI from user string: {}", entry.get("user"), ex);
×
154
            return;
×
155
        }
3✔
156
        String pubkeyhash = entry.get("pubkeyHash");
12✔
157
        boolean approved = approvedIdPubkeyhashMap.containsKey(userIri) && approvedIdPubkeyhashMap.get(userIri).contains(pubkeyhash);
51✔
158
        boolean authoritative = "true".equals(entry.get("authoritative"));
18✔
159
        IRI introNpIri = null;
6✔
160
        try {
161
            introNpIri = vf.createIRI(entry.get("intronp"));
18✔
162
        } catch (IllegalArgumentException ex) {
×
163
            logger.error("Error creating IRI from intronp string: {}", entry.get("intronp"), ex);
×
164
        }
3✔
165
        IRI keyLocation = null;
6✔
166
        try {
167
            if (!entry.get("keyLocation").isEmpty()) {
15✔
168
                keyLocation = vf.createIRI(entry.get("keyLocation"));
18✔
169
            }
170
        } catch (IllegalArgumentException ex) {
×
171
            logger.error("Error creating IRI from keyLocation string: {}", entry.get("keyLocation"), ex);
×
172
        }
3✔
173
        if (approved) {
6✔
174
            if (authoritative) {
6✔
175
                if (introNpIri != null) approvedIntros.add(introNpIri);
21!
176
                if (keyLocation != null) addValue(approvedPubkeyhashLocationMap, pubkeyhash, keyLocation);
27✔
177
            }
178
        } else {
179
            addValue(unapprovedIdPubkeyhashMap, userIri, entry.get("pubkeyHash"));
24✔
180
            addValue(unapprovedPubkeyhashIdMap, entry.get("pubkeyHash"), userIri);
24✔
181
            if (keyLocation != null) addValue(unapprovedPubkeyhashLocationMap, pubkeyhash, keyLocation);
24✔
182
        }
183
        if (introNpIri != null) {
6!
184
            addValue(pubkeyhashIntroMap, entry.get("pubkeyHash"), introNpIri);
24✔
185
        }
186
        String name = entry.get("name");
12✔
187
        if (!name.isEmpty() && !idNameMap.containsKey(userIri)) {
24✔
188
            idNameMap.put(userIri, name);
18✔
189
        }
190
        if ("true".equals(entry.get("isSoftware"))) {
18✔
191
            softwareIdMap.put(userIri, true);
21✔
192
        }
193

194

195
    }
3✔
196

197
/*
198
    private void register(String npId, boolean approved) {
199
        if (!TrustyUriUtils.isPotentialTrustyUri(npId)) return;
200
        IntroNanopub introNp = toIntroNanopub(npId);
201
        if (introNp == null) {
202
            //logger.error("No latest version of introduction found");
203
            return;
204
        }
205
        if (introNp.getUser() == null) {
206
            //logger.error("No identifier found in introduction");
207
            return;
208
        }
209
        if (introNp.getKeyDeclarations().isEmpty()) {
210
            //logger.error("No key declarations found in introduction");
211
            return;
212
        }
213
        if (approved) {
214
            approvedIntroMap.put(introNp.getNanopub().getUri(), introNp);
215
        }
216
        String userId = introNp.getUser().stringValue();
217
        IRI userIri = Utils.vf.createIRI(userId);
218
        if (userId.startsWith("https://orcid.org/")) {
219
            // Some simple ORCID ID wellformedness check:
220
            if (!userId.matches("https://orcid.org/[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]")) return;
221
        }
222
        for (KeyDeclaration kd : introNp.getKeyDeclarations()) {
223
            String pubkey = kd.getPublicKeyString();
224
            IRI keyLocation = kd.getKeyLocation();
225
            if (approved) {
226
                addValue(approvedIdPubkeyMap, userIri, pubkey);
227
                addValue(approvedPubkeyIdMap, pubkey, userIri);
228
                if (keyLocation != null) addValue(approvedPubkeyLocationMap, pubkey, keyLocation);
229
            } else {
230
                if (!hasValue(approvedIdPubkeyMap, userIri, pubkey)) {
231
                    addValue(unapprovedIdPubkeyMap, userIri, pubkey);
232
                    addValue(unapprovedPubkeyIdMap, pubkey, userIri);
233
                    if (keyLocation != null) addValue(unapprovedPubkeyLocationMap, pubkey, keyLocation);
234
                }
235
                addValue(pubkeyIntroMap, pubkey, introNp.getNanopub().getUri());
236
            }
237
        }
238
        if (!idNameMap.containsKey(userIri)) {
239
            idNameMap.put(userIri, introNp.getName());
240
        }
241
    }
242
    */
243

244
    private void addValue(Map<IRI, Set<String>> map, IRI key, String value) {
245
        Set<String> values = map.get(key);
15✔
246
        if (values == null) {
6✔
247
            values = new HashSet<>();
12✔
248
            map.put(key, values);
15✔
249
        }
250
        values.add(value);
12✔
251
    }
3✔
252

253
    private void addValue(Map<String, Set<IRI>> map, String key, IRI value) {
254
        Set<IRI> values = map.get(key);
15✔
255
        if (values == null) {
6✔
256
            values = new HashSet<>();
12✔
257
            map.put(key, values);
15✔
258
        }
259
        values.add(value);
12✔
260
    }
3✔
261

262
    private boolean hasValue(Map<IRI, Set<String>> map, IRI key, String value) {
263
        Set<String> values = map.get(key);
×
264
        if (values == null) return false;
×
265
        return values.contains(value);
×
266
    }
267

268
//        private static boolean hasValue(Map<String,Set<IRI>> map, String key, IRI value) {
269
//                Set<IRI> values = map.get(key);
270
//                if (values == null) return false;
271
//                return values.contains(value);
272
//        }
273

274
    /**
275
     * Checks if the given IRI is a user identifier.
276
     *
277
     * @param userIri the IRI to check
278
     * @return true if the IRI is a user identifier, false otherwise
279
     */
280
    public boolean isUser(IRI userIri) {
281
        return approvedIdPubkeyhashMap.containsKey(userIri) || unapprovedIdPubkeyhashMap.containsKey(userIri);
36!
282
    }
283

284
    /**
285
     * Checks if the given IRI is a software identifier.
286
     *
287
     * @param userIri the IRI to check
288
     * @return true if the IRI is a software identifier, false otherwise
289
     */
290
    public boolean isSoftware(IRI userIri) {
291
        return softwareIdMap.containsKey(userIri) && softwareIdMap.get(userIri);
×
292
    }
293

294
    /**
295
     * Checks if the given userId is a valid user identifier.
296
     *
297
     * @param userId the user identifier to check, must start with "https://" or "http://"
298
     * @return true if the userId is a valid user identifier, false otherwise
299
     */
300
    public boolean isUser(String userId) {
301
        if (!userId.startsWith("https://") && !userId.startsWith("http://")) {
24!
302
            return false;
×
303
        }
304
        try {
305
            IRI userIri = Utils.vf.createIRI(userId);
12✔
306
            return isUser(userIri);
12✔
307
        } catch (IllegalArgumentException ex) {
×
308
            return false;
×
309
        }
310
    }
311

312
    /**
313
     * Checks if the given public key is approved for the specified user.
314
     *
315
     * @param pubkeyHash the public key to check
316
     * @param user       the IRI of the user to check against
317
     * @return true if the key is approved for the user, false otherwise
318
     */
319
    public boolean isApprovedPubkeyHashForUser(String pubkeyHash, IRI user) {
320
        return hasValue(approvedIdPubkeyhashMap, user, pubkeyHash);
×
321
    }
322

323
    private String getShortName(IRI userIri) {
324
        if (userIri == null) return "(unknown)";
×
325
        String n = userIri.stringValue();
×
326
        n = n.replaceFirst("^https://orcid.org/", "");
×
327
        if (n.length() > 40) return n.substring(0, 30) + "...";
×
328
        return n;
×
329
    }
330

331
    /**
332
     * Retrieves the IRI of a user based on their public key.
333
     *
334
     * @param pubkeyHash   the public key of the user
335
     * @param approvedOnly if true, only approved users are considered; if false, unapproved users are also considered
336
     * @return the IRI of the user if found, or null if not found
337
     */
338
    public IRI getUserIriForPubkeyHash(String pubkeyHash, boolean approvedOnly) {
339
        Set<IRI> userIris = approvedPubkeyhashIdMap.get(pubkeyHash);
×
340
        if (userIris != null && userIris.size() == 1) return userIris.iterator().next();
×
341
        if (!approvedOnly) {
×
342
            userIris = unapprovedPubkeyhashIdMap.get(pubkeyHash);
×
343
            if (userIris != null && userIris.size() == 1) return userIris.iterator().next();
×
344
        }
345
        return null;
×
346
    }
347

348
    /**
349
     * Retrieves the IRI of the owner of a nanopublication's signature.
350
     *
351
     * @param np the nanopublication
352
     * @return the IRI of the signature owner if found, or null if not found or if an error occurs
353
     */
354
    public IRI getSignatureOwnerIri(Nanopub np) {
355
        try {
356
            if (np != null) {
6!
357
                NanopubSignatureElement se = SignatureUtils.getSignatureElement(np);
9✔
358
                if (se != null) {
6!
359
                    String pubkeyHash = Utils.createSha256HexHash(se.getPublicKeyString());
×
360
                    return getUserIriForPubkeyHash(pubkeyHash, true);
×
361
                }
362
            }
363
        } catch (MalformedCryptoElementException ex) {
×
364
            logger.error("Error getting signature element", ex);
×
365
        }
3✔
366
        return null;
6✔
367
    }
368

369
    /**
370
     * Retrieves the name of a user based on their IRI.
371
     *
372
     * @param userIri the IRI of the user
373
     * @return the name of the user if found, or null if not found
374
     */
375
    public String getName(IRI userIri) {
376
        return idNameMap.get(userIri);
18✔
377
    }
378

379
    /**
380
     * Retrieves the display name of a user based on their IRI.
381
     *
382
     * @param userIri the IRI of the user
383
     * @return the display name of the user, which includes their name and short name
384
     */
385
    public String getDisplayName(IRI userIri) {
386
        String name = getName(userIri);
×
387
        if (name != null && !name.isEmpty()) {
×
388
            return name + " (" + getShortName(userIri) + ")";
×
389
        }
390
        return getShortName(userIri);
×
391
    }
392

393
    /**
394
     * Retrieves a short display name for a user based on their IRI.
395
     *
396
     * @param userIri the IRI of the user
397
     * @return the short display name of the user, which is either their name or their short name
398
     */
399
    public String getShortDisplayName(IRI userIri) {
400
        String name = getName(userIri);
×
401
        if (name != null && !name.isEmpty()) {
×
402
            return name;
×
403
        }
404
        return getShortName(userIri);
×
405
    }
406

407
    /**
408
     * Retrieves a short display name for a user based on their IRI and public key.
409
     *
410
     * @param userIri    the IRI of the user
411
     * @param pubkeyHash the public key of the user
412
     * @return the short display name of the user, which may include a contested identity note if multiple identities are associated with the public key
413
     */
414
    public String getShortDisplayNameForPubkeyHash(IRI userIri, String pubkeyHash) {
415
        Set<IRI> ids = approvedPubkeyhashIdMap.get(pubkeyHash);
×
416
        if (ids == null || ids.isEmpty()) {
×
417
            ids = unapprovedPubkeyhashIdMap.get(pubkeyHash);
×
418
            if (ids == null || ids.isEmpty()) {
×
419
                return getShortName(userIri);
×
420
            } else if (ids.size() == 1) {
×
421
                return getShortDisplayName(ids.iterator().next());
×
422
            } else {
423
                // Not showing "contested identity" for now.
424
                return getShortName(userIri);  // + " (contested identity)";
×
425
            }
426
        } else if (ids.size() == 1) {
×
427
            return getShortDisplayName(ids.iterator().next());
×
428
        } else {
429
            // Not showing "contested identity" for now.
430
            //return "(contested identity)";
431
            return getShortDisplayName(ids.iterator().next());
×
432
        }
433
    }
434

435
    /**
436
     * Finds a single user ID for a given public key.
437
     *
438
     * @param pubkeyHash the public key to search for
439
     * @return the IRI of the user if exactly one ID is found for the public key, or null if no ID or multiple IDs are found
440
     */
441
    public IRI findSingleIdForPubkeyHash(String pubkeyHash) {
442
        if (approvedPubkeyhashIdMap.containsKey(pubkeyHash) && !approvedPubkeyhashIdMap.get(pubkeyHash).isEmpty()) {
×
443
            if (approvedPubkeyhashIdMap.get(pubkeyHash).size() == 1) {
×
444
                return approvedPubkeyhashIdMap.get(pubkeyHash).iterator().next();
×
445
            } else {
446
                return null;
×
447
            }
448
        }
449
        if (unapprovedPubkeyhashIdMap.containsKey(pubkeyHash) && !unapprovedPubkeyhashIdMap.get(pubkeyHash).isEmpty()) {
×
450
            if (unapprovedPubkeyhashIdMap.get(pubkeyHash).size() == 1) {
×
451
                return unapprovedPubkeyhashIdMap.get(pubkeyHash).iterator().next();
×
452
            } else {
453
                return null;
×
454
            }
455
        }
456
        return null;
×
457
    }
458

459
    /**
460
     * Comparator for sorting users by their display names in a case-insensitive manner.
461
     */
462
    public final transient Comparator<IRI> userComparator = (iri1, iri2) -> {
12✔
463
        String name1 = getName(iri1);
×
464
        String name2 = getName(iri2);
×
465
        boolean hasName1 = name1 != null && !name1.isEmpty();
×
466
        boolean hasName2 = name2 != null && !name2.isEmpty();
×
467
        if (hasName1 != hasName2) {
×
468
            return hasName1 ? -1 : 1;
×
469
        }
470
        return getDisplayName(iri1).toLowerCase().compareTo(getDisplayName(iri2).toLowerCase());
×
471
    };
472

473
    /**
474
     * Retrieves a list of users, either approved or unapproved.
475
     *
476
     * @param approved if true, retrieves approved users; if false, retrieves unapproved users
477
     * @return a sorted list of user IRIs
478
     */
479
    public List<IRI> getUsers(boolean approved) {
480
        List<IRI> list;
481
        if (approved) {
×
482
            list = new ArrayList<IRI>(approvedIdPubkeyhashMap.keySet());
×
483
        } else {
484
            list = new ArrayList<IRI>();
×
485
            for (IRI u : unapprovedIdPubkeyhashMap.keySet()) {
×
486
                if (!approvedIdPubkeyhashMap.containsKey(u)) list.add(u);
×
487
            }
×
488
        }
489
        // TODO Cache the sorted list to not sort from scratch each time:
490
        list.sort(userComparator);
×
491
        return list;
×
492
    }
493

494
    /**
495
     * Retrieves a list of public keys for a given user.
496
     *
497
     * @param user     the IRI of the user whose public keys are to be retrieved
498
     * @param approved if true, retrieves approved public keys; if false, retrieves unapproved public keys
499
     * @return a list of public keys associated with the user, either approved or unapproved
500
     */
501
    public List<String> getPubkeyHashes(IRI user, Boolean approved) {
502
        List<String> pubkeys = new ArrayList<>();
12✔
503
        if (user != null) {
6!
504
            if (approved == null || approved) {
15!
505
                if (approvedIdPubkeyhashMap.containsKey(user)) pubkeys.addAll(approvedIdPubkeyhashMap.get(user));
39!
506
            }
507
            if (approved == null || !approved) {
15!
508
                if (unapprovedIdPubkeyhashMap.containsKey(user)) pubkeys.addAll(unapprovedIdPubkeyhashMap.get(user));
39✔
509
            }
510
        }
511
        return pubkeys;
6✔
512
    }
513

514
    /**
515
     * Retrieves a list of introduction nanopublications for a given user.
516
     *
517
     * @param user the IRI of the user whose introduction nanopublications are to be retrieved
518
     * @return a list of introduction nanopublications associated with the user, sorted by creation time
519
     */
520
    public List<IntroNanopub> getIntroNanopubs(IRI user) {
521
        if (introNanopubLists.containsKey(user)) return introNanopubLists.get(user);
×
522

523
        Map<IRI, IntroNanopub> introNps = new HashMap<>();
×
524
        if (approvedIdPubkeyhashMap.containsKey(user)) {
×
525
            for (String pk : approvedIdPubkeyhashMap.get(user)) {
×
526
                getIntroNanopubs(pk, introNps);
×
527
            }
×
528
        }
529
        if (unapprovedIdPubkeyhashMap.containsKey(user)) {
×
530
            for (String pk : unapprovedIdPubkeyhashMap.get(user)) {
×
531
                getIntroNanopubs(pk, introNps);
×
532
            }
×
533
        }
534
        List<IntroNanopub> list = new ArrayList<>(introNps.values());
×
535
        list.sort((i0, i1) -> {
×
536
            Calendar c0 = SimpleTimestampPattern.getCreationTime(i0.getNanopub());
×
537
            Calendar c1 = SimpleTimestampPattern.getCreationTime(i1.getNanopub());
×
538
            if (c0 == null && c1 == null) return 0;
×
539
            if (c0 == null) return 1;
×
540
            if (c1 == null) return -1;
×
541
            return -c0.compareTo(c1);
×
542
        });
543
        introNanopubLists.put(user, list);
×
544
        return list;
×
545
    }
546

547
    /**
548
     * Retrieves a map of introduction nanopublications for a given public key.
549
     *
550
     * @param pubkey the public key for which introduction nanopublications are to be retrieved
551
     * @return a map where the keys are IRI identifiers of introduction nanopublications and the values are the corresponding IntroNanopub objects
552
     */
553
    public Map<IRI, IntroNanopub> getIntroNanopubs(String pubkey) {
554
        Map<IRI, IntroNanopub> introNps = new HashMap<>();
×
555
        getIntroNanopubs(pubkey, introNps);
×
556
        return introNps;
×
557
    }
558

559
    private void getIntroNanopubs(String pubkeyhash, Map<IRI, IntroNanopub> introNps) {
560
        if (pubkeyhashIntroMap.containsKey(pubkeyhash)) {
×
561
            for (IRI iri : pubkeyhashIntroMap.get(pubkeyhash)) {
×
562
                IntroNanopub introNp = toIntroNanopub(iri);
×
563
                if (introNp != null) {
×
564
                    introNps.put(iri, introNp);
×
565
                }
566
            }
×
567
        }
568
    }
×
569

570
    /**
571
     * Checks if the given introduction nanopublication is approved.
572
     *
573
     * @param in the introduction nanopublication to check
574
     * @return true if the introduction nanopublication is approved, false otherwise
575
     */
576
    public boolean isApproved(IntroNanopub in) {
577
        return approvedIntros.contains(in.getNanopub().getUri());
×
578
    }
579

580
    /**
581
     * Retrieves the location of a public key.
582
     *
583
     * @param pubkeyHash the public key for which the location is to be retrieved
584
     * @return the IRI of the key location if found, or null if not found or if multiple locations are associated with the key
585
     */
586
    public IRI getKeyLocationForPubkeyHash(String pubkeyHash) {
587
        if (approvedPubkeyhashLocationMap.containsKey(pubkeyHash) && !approvedPubkeyhashLocationMap.get(pubkeyHash).isEmpty()) {
×
588
            if (approvedPubkeyhashLocationMap.get(pubkeyHash).size() == 1)
×
589
                return approvedPubkeyhashLocationMap.get(pubkeyHash).iterator().next();
×
590
            return null;
×
591
        }
592
        if (unapprovedPubkeyhashLocationMap.containsKey(pubkeyHash) && unapprovedPubkeyhashLocationMap.get(pubkeyHash).size() == 1) {
×
593
            return unapprovedPubkeyhashLocationMap.get(pubkeyHash).iterator().next();
×
594
        }
595
        return null;
×
596
    }
597

598
    /**
599
     * Retrieves the profile picture IRI for a user based on their IRI.
600
     *
601
     * @param userIri the IRI of the user for whom to retrieve the profile picture
602
     * @return the IRI of the user's profile picture if found, or null if not found
603
     */
604
    public IRI getProfilePicture(IRI userIri) {
605
        return profilePictures.get(userIri);
×
606
    }
607

608
    /**
609
     * Retrieves the default license IRI for a user based on their IRI.
610
     *
611
     * @param userIri the IRI of the user for whom to retrieve the default license
612
     * @return the IRI of the user's default license if found, or null if not found
613
     */
614
    public IRI getDefaultLicense(IRI userIri) {
615
        return defaultLicense.get(userIri);
×
616
    }
617

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