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

knowledgepixels / nanodash / 22760940566

06 Mar 2026 11:11AM UTC coverage: 15.877% (+0.009%) from 15.868%
22760940566

push

github

web-flow
Merge pull request #379 from knowledgepixels/222-profile-logo

Users can set a profile picture

705 of 5393 branches covered (13.07%)

Branch coverage included in aggregate %.

1741 of 10013 relevant lines covered (17.39%)

2.37 hits per line

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

36.58
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

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

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

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

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

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

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

127
    private IntroNanopub toIntroNanopub(IRI iri) {
128
        if (iri == null) return null;
×
129
        if (introMap.containsKey(iri)) return introMap.get(iri);
×
130
        Nanopub np = Utils.getNanopub(iri.stringValue());
×
131
        if (np == null) return null;
×
132
        IntroNanopub introNp = new IntroNanopub(np);
×
133
        introMap.put(np.getUri(), introNp);
×
134
        return introNp;
×
135
    }
136

137
    private void registerApproved(RegistryAccountInfo rai) {
138
        if (rai.getAgent().equals("$")) return;
18✔
139
        addValue(approvedIdPubkeyhashMap, rai.getAgentIri(), rai.getPubkey());
24✔
140
        addValue(approvedPubkeyhashIdMap, rai.getPubkey(), rai.getAgentIri());
24✔
141
    }
3✔
142

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

189

190
    }
3✔
191

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

239
    private void addValue(Map<IRI, Set<String>> map, IRI key, String value) {
240
        Set<String> values = map.get(key);
15✔
241
        if (values == null) {
6✔
242
            values = new HashSet<>();
12✔
243
            map.put(key, values);
15✔
244
        }
245
        values.add(value);
12✔
246
    }
3✔
247

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

257
    private boolean hasValue(Map<IRI, Set<String>> map, IRI key, String value) {
258
        Set<String> values = map.get(key);
×
259
        if (values == null) return false;
×
260
        return values.contains(value);
×
261
    }
262

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

269
    /**
270
     * Checks if the given IRI is a user identifier.
271
     *
272
     * @param userIri the IRI to check
273
     * @return true if the IRI is a user identifier, false otherwise
274
     */
275
    public boolean isUser(IRI userIri) {
276
        return approvedIdPubkeyhashMap.containsKey(userIri) || unapprovedIdPubkeyhashMap.containsKey(userIri);
×
277
    }
278

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

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

307
    /**
308
     * Checks if the given public key is approved for the specified user.
309
     *
310
     * @param pubkeyhash the public key to check
311
     * @param user       the IRI of the user to check against
312
     * @return true if the key is approved for the user, false otherwise
313
     */
314
    public boolean isApprovedPubkeyhashForUser(String pubkeyhash, IRI user) {
315
        return hasValue(approvedIdPubkeyhashMap, user, pubkeyhash);
×
316
    }
317

318
    private String getShortName(IRI userIri) {
319
        if (userIri == null) return "(unknown)";
×
320
        String n = userIri.stringValue();
×
321
        n = n.replaceFirst("^https://orcid.org/", "");
×
322
        if (n.length() > 40) return n.substring(0, 30) + "...";
×
323
        return n;
×
324
    }
325

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

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

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

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

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

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

430
    /**
431
     * Finds a single user ID for a given public key.
432
     *
433
     * @param pubkeyhash the public key to search for
434
     * @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
435
     */
436
    public IRI findSingleIdForPubkeyhash(String pubkeyhash) {
437
        if (approvedPubkeyhashIdMap.containsKey(pubkeyhash) && !approvedPubkeyhashIdMap.get(pubkeyhash).isEmpty()) {
×
438
            if (approvedPubkeyhashIdMap.get(pubkeyhash).size() == 1) {
×
439
                return approvedPubkeyhashIdMap.get(pubkeyhash).iterator().next();
×
440
            } else {
441
                return null;
×
442
            }
443
        }
444
        if (unapprovedPubkeyhashIdMap.containsKey(pubkeyhash) && !unapprovedPubkeyhashIdMap.get(pubkeyhash).isEmpty()) {
×
445
            if (unapprovedPubkeyhashIdMap.get(pubkeyhash).size() == 1) {
×
446
                return unapprovedPubkeyhashIdMap.get(pubkeyhash).iterator().next();
×
447
            } else {
448
                return null;
×
449
            }
450
        }
451
        return null;
×
452
    }
453

454
    /**
455
     * Comparator for sorting users by their display names in a case-insensitive manner.
456
     */
457
    public final transient Comparator<IRI> userComparator = (iri1, iri2) -> getDisplayName(iri1).toLowerCase().compareTo(getDisplayName(iri2).toLowerCase());
12✔
458

459
    /**
460
     * Retrieves a list of users, either approved or unapproved.
461
     *
462
     * @param approved if true, retrieves approved users; if false, retrieves unapproved users
463
     * @return a sorted list of user IRIs
464
     */
465
    public List<IRI> getUsers(boolean approved) {
466
        List<IRI> list;
467
        if (approved) {
×
468
            list = new ArrayList<IRI>(approvedIdPubkeyhashMap.keySet());
×
469
        } else {
470
            list = new ArrayList<IRI>();
×
471
            for (IRI u : unapprovedIdPubkeyhashMap.keySet()) {
×
472
                if (!approvedIdPubkeyhashMap.containsKey(u)) list.add(u);
×
473
            }
×
474
        }
475
        // TODO Cache the sorted list to not sort from scratch each time:
476
        list.sort(userComparator);
×
477
        return list;
×
478
    }
479

480
    /**
481
     * Retrieves a list of public keys for a given user.
482
     *
483
     * @param user     the IRI of the user whose public keys are to be retrieved
484
     * @param approved if true, retrieves approved public keys; if false, retrieves unapproved public keys
485
     * @return a list of public keys associated with the user, either approved or unapproved
486
     */
487
    public List<String> getPubkeyhashes(IRI user, Boolean approved) {
488
        List<String> pubkeys = new ArrayList<>();
12✔
489
        if (user != null) {
6!
490
            if (approved == null || approved) {
15!
491
                if (approvedIdPubkeyhashMap.containsKey(user)) pubkeys.addAll(approvedIdPubkeyhashMap.get(user));
39✔
492
            }
493
            if (approved == null || !approved) {
15!
494
                if (unapprovedIdPubkeyhashMap.containsKey(user)) pubkeys.addAll(unapprovedIdPubkeyhashMap.get(user));
×
495
            }
496
        }
497
        return pubkeys;
6✔
498
    }
499

500
    /**
501
     * Retrieves a list of introduction nanopublications for a given user.
502
     *
503
     * @param user the IRI of the user whose introduction nanopublications are to be retrieved
504
     * @return a list of introduction nanopublications associated with the user, sorted by creation time
505
     */
506
    public List<IntroNanopub> getIntroNanopubs(IRI user) {
507
        if (introNanopubLists.containsKey(user)) return introNanopubLists.get(user);
×
508

509
        Map<IRI, IntroNanopub> introNps = new HashMap<>();
×
510
        if (approvedIdPubkeyhashMap.containsKey(user)) {
×
511
            for (String pk : approvedIdPubkeyhashMap.get(user)) {
×
512
                getIntroNanopubs(pk, introNps);
×
513
            }
×
514
        }
515
        if (unapprovedIdPubkeyhashMap.containsKey(user)) {
×
516
            for (String pk : unapprovedIdPubkeyhashMap.get(user)) {
×
517
                getIntroNanopubs(pk, introNps);
×
518
            }
×
519
        }
520
        List<IntroNanopub> list = new ArrayList<>(introNps.values());
×
521
        list.sort((i0, i1) -> {
×
522
            Calendar c0 = SimpleTimestampPattern.getCreationTime(i0.getNanopub());
×
523
            Calendar c1 = SimpleTimestampPattern.getCreationTime(i1.getNanopub());
×
524
            if (c0 == null && c1 == null) return 0;
×
525
            if (c0 == null) return 1;
×
526
            if (c1 == null) return -1;
×
527
            return -c0.compareTo(c1);
×
528
        });
529
        introNanopubLists.put(user, list);
×
530
        return list;
×
531
    }
532

533
    /**
534
     * Retrieves a map of introduction nanopublications for a given public key.
535
     *
536
     * @param pubkey the public key for which introduction nanopublications are to be retrieved
537
     * @return a map where the keys are IRI identifiers of introduction nanopublications and the values are the corresponding IntroNanopub objects
538
     */
539
    public Map<IRI, IntroNanopub> getIntroNanopubs(String pubkey) {
540
        Map<IRI, IntroNanopub> introNps = new HashMap<>();
×
541
        getIntroNanopubs(pubkey, introNps);
×
542
        return introNps;
×
543
    }
544

545
    private void getIntroNanopubs(String pubkeyhash, Map<IRI, IntroNanopub> introNps) {
546
        if (pubkeyhashIntroMap.containsKey(pubkeyhash)) {
×
547
            for (IRI iri : pubkeyhashIntroMap.get(pubkeyhash)) {
×
548
                IntroNanopub introNp = toIntroNanopub(iri);
×
549
                if (introNp != null) {
×
550
                    introNps.put(iri, introNp);
×
551
                }
552
            }
×
553
        }
554
    }
×
555

556
    /**
557
     * Checks if the given introduction nanopublication is approved.
558
     *
559
     * @param in the introduction nanopublication to check
560
     * @return true if the introduction nanopublication is approved, false otherwise
561
     */
562
    public boolean isApproved(IntroNanopub in) {
563
        return approvedIntros.contains(in.getNanopub().getUri());
×
564
    }
565

566
    /**
567
     * Retrieves the location of a public key.
568
     *
569
     * @param pubkeyhash the public key for which the location is to be retrieved
570
     * @return the IRI of the key location if found, or null if not found or if multiple locations are associated with the key
571
     */
572
    public IRI getKeyLocationForPubkeyhash(String pubkeyhash) {
573
        if (approvedPubkeyhashLocationMap.containsKey(pubkeyhash) && !approvedPubkeyhashLocationMap.get(pubkeyhash).isEmpty()) {
×
574
            if (approvedPubkeyhashLocationMap.get(pubkeyhash).size() == 1)
×
575
                return approvedPubkeyhashLocationMap.get(pubkeyhash).iterator().next();
×
576
            return null;
×
577
        }
578
        if (unapprovedPubkeyhashLocationMap.containsKey(pubkeyhash) && unapprovedPubkeyhashLocationMap.get(pubkeyhash).size() == 1) {
×
579
            return unapprovedPubkeyhashLocationMap.get(pubkeyhash).iterator().next();
×
580
        }
581
        return null;
×
582
    }
583

584
    public IRI getProfilePicture(IRI userIri) {
585
        return profilePictures.get(userIri);
×
586
    }
587

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