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

IQSS / dataverse / #22693

03 Jul 2024 01:09PM CUT coverage: 20.626% (-0.09%) from 20.716%
#22693

push

github

web-flow
Merge pull request #10664 from IQSS/develop

merge develop into master for 6.3

195 of 1852 new or added lines in 82 files covered. (10.53%)

72 existing lines in 33 files now uncovered.

17335 of 84043 relevant lines covered (20.63%)

0.21 hits per line

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

0.0
/src/main/java/edu/harvard/iq/dataverse/util/json/JSONLDUtil.java
1
package edu.harvard.iq.dataverse.util.json;
2

3
import java.io.StringReader;
4
import java.io.StringWriter;
5
import java.sql.Timestamp;
6
import java.time.LocalDate;
7
import java.time.LocalDateTime;
8
import java.time.format.DateTimeFormatter;
9
import java.util.ArrayList;
10
import java.util.Arrays;
11
import java.util.HashMap;
12
import java.util.LinkedList;
13
import java.util.List;
14
import java.util.Map;
15
import java.util.Optional;
16
import java.util.Map.Entry;
17
import java.util.TreeMap;
18
import java.util.logging.Logger;
19

20

21
import jakarta.json.Json;
22
import jakarta.json.JsonArray;
23
import jakarta.json.JsonObject;
24
import jakarta.json.JsonObjectBuilder;
25
import jakarta.json.JsonString;
26
import jakarta.json.JsonValue;
27
import jakarta.json.JsonWriter;
28
import jakarta.json.JsonWriterFactory;
29
import jakarta.json.JsonValue.ValueType;
30
import jakarta.json.stream.JsonGenerator;
31
import jakarta.ws.rs.BadRequestException;
32

33
import edu.harvard.iq.dataverse.ControlledVocabularyValue;
34
import edu.harvard.iq.dataverse.Dataset;
35
import edu.harvard.iq.dataverse.DatasetField;
36
import edu.harvard.iq.dataverse.DatasetFieldCompoundValue;
37
import edu.harvard.iq.dataverse.DatasetFieldServiceBean;
38
import edu.harvard.iq.dataverse.DatasetFieldType;
39
import edu.harvard.iq.dataverse.DatasetFieldValue;
40
import edu.harvard.iq.dataverse.DatasetVersion;
41
import edu.harvard.iq.dataverse.GlobalId;
42
import edu.harvard.iq.dataverse.MetadataBlock;
43
import edu.harvard.iq.dataverse.MetadataBlockServiceBean;
44
import edu.harvard.iq.dataverse.TermsOfUseAndAccess;
45
import org.apache.commons.lang3.StringUtils;
46

47
import com.apicatalog.jsonld.JsonLd;
48
import com.apicatalog.jsonld.JsonLdError;
49
import com.apicatalog.jsonld.document.JsonDocument;
50

51
import edu.harvard.iq.dataverse.DatasetVersion.VersionState;
52
import edu.harvard.iq.dataverse.license.License;
53
import edu.harvard.iq.dataverse.license.LicenseServiceBean;
54
import edu.harvard.iq.dataverse.pidproviders.PidProvider;
55
import jakarta.json.JsonReader;
56

57
public class JSONLDUtil {
×
58

59
    private static final Logger logger = Logger.getLogger(JSONLDUtil.class.getCanonicalName());
×
60

61
    /*
62
     * private static Map<String, String> populateContext(JsonValue json) {
63
     * Map<String, String> context = new TreeMap<String, String>(); if (json
64
     * instanceof JsonArray) { logger.warning("Array @context not yet supported"); }
65
     * else { for (String key : ((JsonObject) json).keySet()) {
66
     * context.putIfAbsent(key, ((JsonObject) json).getString(key)); } } return
67
     * context; }
68
     */
69

70
    public static JsonObject getContext(Map<String, String> contextMap) {
71
        JsonObjectBuilder contextBuilder = Json.createObjectBuilder();
×
72
        for (Entry<String, String> e : contextMap.entrySet()) {
×
73
            contextBuilder.add(e.getKey(), e.getValue());
×
74
        }
×
75
        return contextBuilder.build();
×
76
    }
77

78
    public static Dataset updateDatasetMDFromJsonLD(Dataset ds, String jsonLDBody,
79
            MetadataBlockServiceBean metadataBlockSvc, DatasetFieldServiceBean datasetFieldSvc, boolean append,
80
            boolean migrating, LicenseServiceBean licenseSvc) {
81

82
        DatasetVersion dsv = new DatasetVersion();
×
83

84
        JsonObject jsonld = decontextualizeJsonLD(jsonLDBody);
×
85
        if (migrating) {
×
86
            Optional<GlobalId> maybePid = PidProvider.parse(jsonld.getString("@id"));
×
87
            if (maybePid.isPresent()) {
×
88
                ds.setGlobalId(maybePid.get());
×
89
            } else {
90
                // unparsable PID passed. Terminate.
91
                throw new BadRequestException("Cannot parse the @id '" + jsonld.getString("@id")
×
92
                        + "'. Make sure it is in valid form - see Dataverse Native API documentation.");
93
            }
94
        }
95
        
96
        //Store the metadatalanguage if sent - the caller needs to check whether it is allowed (as with any GlobalID)
97
        ds.setMetadataLanguage(jsonld.getString(JsonLDTerm.schemaOrg("inLanguage").getUrl(),null));
×
98

99
        dsv = updateDatasetVersionMDFromJsonLD(dsv, jsonld, metadataBlockSvc, datasetFieldSvc, append, migrating, licenseSvc);
×
100
        dsv.setDataset(ds);
×
101

102
        List<DatasetVersion> versions = new ArrayList<>(1);
×
103
        versions.add(dsv);
×
104

105
        ds.setVersions(versions);
×
106
        if (migrating) {
×
107
            if (jsonld.containsKey(JsonLDTerm.schemaOrg("dateModified").getUrl())) {
×
108
                String dateString = jsonld.getString(JsonLDTerm.schemaOrg("dateModified").getUrl());
×
109
                LocalDateTime dateTime = getDateTimeFrom(dateString);
×
110
                ds.setModificationTime(Timestamp.valueOf(dateTime));
×
111
            }
112
        }
113
        return ds;
×
114
    }
115

116
    public static DatasetVersion updateDatasetVersionMDFromJsonLD(DatasetVersion dsv, String jsonLDBody,
117
            MetadataBlockServiceBean metadataBlockSvc, DatasetFieldServiceBean datasetFieldSvc, boolean append, boolean migrating, LicenseServiceBean licenseSvc) {
118
        JsonObject jsonld = decontextualizeJsonLD(jsonLDBody);
×
119
        return updateDatasetVersionMDFromJsonLD(dsv, jsonld, metadataBlockSvc, datasetFieldSvc, append, migrating, licenseSvc);
×
120
    }
121

122
    /**
123
     * 
124
     * @param dsv
125
     * @param jsonld
126
     * @param metadataBlockSvc
127
     * @param datasetFieldSvc
128
     * @param append           - if append, will add new top level field values for
129
     *                         multi-valued fields, if true and field type isn't
130
     *                         multiple, will fail. if false will replace all
131
     *                         value(s) for fields found in the json-ld.
132
     * @return
133
     */
134
    public static DatasetVersion updateDatasetVersionMDFromJsonLD(DatasetVersion dsv, JsonObject jsonld,
135
            MetadataBlockServiceBean metadataBlockSvc, DatasetFieldServiceBean datasetFieldSvc, boolean append, boolean migrating, LicenseServiceBean licenseSvc) {
136

137
        //Assume draft to start
138
        dsv.setVersionState(VersionState.DRAFT);
×
139
        
140
        populateFieldTypeMap(metadataBlockSvc);
×
141

142
        // get existing ones?
143
        List<DatasetField> dsfl = dsv.getDatasetFields();
×
144
        Map<DatasetFieldType, DatasetField> fieldByTypeMap = new HashMap<DatasetFieldType, DatasetField>();
×
145
        for (DatasetField dsf : dsfl) {
×
146
            if (fieldByTypeMap.containsKey(dsf.getDatasetFieldType())) {
×
147
                // May have multiple values per field, but not multiple fields of one type?
148
                logger.warning("Multiple fields of type " + dsf.getDatasetFieldType().getName());
×
149
            }
150
            fieldByTypeMap.put(dsf.getDatasetFieldType(), dsf);
×
151
        }
×
152

153
        TermsOfUseAndAccess terms = (dsv.getTermsOfUseAndAccess() != null) ? dsv.getTermsOfUseAndAccess().copyTermsOfUseAndAccess() : new TermsOfUseAndAccess();
×
154

155

156
        for (String key : jsonld.keySet()) {
×
157
            if (!key.equals("@context")) {
×
158
                if (dsftMap.containsKey(key)) {
×
159

160
                    DatasetFieldType dsft = dsftMap.get(key);
×
161
                    DatasetField dsf = null;
×
162
                    if (fieldByTypeMap.containsKey(dsft)) {
×
163
                        dsf = fieldByTypeMap.get(dsft);
×
164
                        // If there's an existing field, we use it with append and remove it for !append
165
                        // unless it's multiple
166
                        if (!append && !dsft.isAllowMultiples()) {
×
167
                            dsfl.remove(dsf);
×
168
                            dsf=null;
×
169
                        }
170
                    }
171
                    if (dsf == null) {
×
172
                        dsf = new DatasetField();
×
173
                        dsfl.add(dsf);
×
174
                        dsf.setDatasetFieldType(dsft);
×
175
                    }
176

177
                    JsonArray valArray = getValues(jsonld.get(key), dsft.isAllowMultiples(), dsft.getName());
×
178

179
                    addField(dsf, valArray, dsft, datasetFieldSvc, append);
×
180

181
                } else {
×
182
                    //When migrating, the publication date and version number can be set
183
                    if (key.equals(JsonLDTerm.schemaOrg("datePublished").getUrl())&& migrating && !append) {
×
184
                        dsv.setVersionState(VersionState.RELEASED);
×
185
                    } else if (key.equals(JsonLDTerm.schemaOrg("version").getUrl())&& migrating && !append) {
×
186
                        String friendlyVersion = jsonld.getString(JsonLDTerm.schemaOrg("version").getUrl());
×
187
                        int index = friendlyVersion.indexOf(".");
×
188
                        if (index > 0) {
×
189
                            dsv.setVersionNumber(Long.parseLong(friendlyVersion.substring(0, index)));
×
190
                            dsv.setMinorVersionNumber(Long.parseLong(friendlyVersion.substring(index + 1)));
×
191
                        }
192
                    }
×
193
                    else if (datasetTerms.contains(key)) {
×
194
                        // Other Dataset-level TermsOfUseAndAccess
195
                        if (!append || !isSet(terms, key)) {
×
196
                            if (key.equals(JsonLDTerm.schemaOrg("license").getUrl())) {
×
197
                                if (jsonld.containsKey(JsonLDTerm.termsOfUse.getUrl())) {
×
198
                                    throw new BadRequestException("Cannot specify " + JsonLDTerm.schemaOrg("license").getUrl() + " and " + JsonLDTerm.termsOfUse.getUrl());
×
199
                                }
200
                                if (StringUtils.isEmpty(jsonld.getString(key))) {
×
201
                                    setSemTerm(terms, key, licenseSvc.getDefault());
×
202
                                }
203
                                else {
204
                                        License license = licenseSvc.getByNameOrUri(jsonld.getString(key));
×
205
                                        if (license == null) throw new BadRequestException("Invalid license");
×
206
                                        setSemTerm(terms, key, license);
×
207
                                }
×
208
                            }
209
                            else if (key.equals("https://dataverse.org/schema/core#fileRequestAccess")) {
×
210
                                setSemTerm(terms, key, jsonld.getBoolean(key));
×
211
                            }
212
                            else {
213
                                setSemTerm(terms, key, jsonld.getString(key));
×
214
                            }
215
                        }
216
                        else {
217
                            throw new BadRequestException("Can't append to a single-value field that already has a value: " + key);
×
218
                        }
219
                    } else if (key.equals(JsonLDTerm.fileTermsOfAccess.getUrl())) {
×
220
                        // Other DataFile-level TermsOfUseAndAccess
221
                        JsonObject fAccessObject = jsonld.getJsonObject(JsonLDTerm.fileTermsOfAccess.getUrl());
×
222
                        for (String fileKey : fAccessObject.keySet()) {
×
223
                            if (datafileTerms.contains(fileKey)) {
×
224
                                if (!append || !isSet(terms, fileKey)) {
×
225
                                    if (fileKey.equals(JsonLDTerm.fileRequestAccess.getUrl())) {
×
226
                                        setSemTerm(terms, fileKey, fAccessObject.getBoolean(fileKey));
×
227
                                    } else {
228
                                        setSemTerm(terms, fileKey, fAccessObject.getString(fileKey));
×
229
                                    }
230
                                } else {
231
                                    throw new BadRequestException(
×
232
                                            "Can't append to a single-value field that already has a value: "
233
                                                    + fileKey);
234
                                }
235
                            }
236
                        }
×
237
                    }
238
                    // ToDo: support Dataverse location metadata? e.g. move to new dataverse?
239
                    // re: JsonLDTerm.schemaOrg("includedInDataCatalog")
240
                }
241
            }
242
        }
×
243
        dsv.setTermsOfUseAndAccess(terms);
×
244
        terms.setDatasetVersion(dsv);
×
245
        dsv.setDatasetFields(dsfl);
×
246

247
        return dsv;
×
248
    }
249
    /**
250
     * 
251
     * @param dsv
252
     * @param jsonLDBody
253
     * @param metadataBlockService
254
     * @param datasetFieldSvc
255
     * @param b
256
     * @param c
257
     * @return
258
     */
259
    public static DatasetVersion deleteDatasetVersionMDFromJsonLD(DatasetVersion dsv, String jsonLDBody,
260
            MetadataBlockServiceBean metadataBlockSvc, LicenseServiceBean licenseSvc) {
261
        logger.fine("deleteDatasetVersionMD");
×
262
        JsonObject jsonld = decontextualizeJsonLD(jsonLDBody);
×
263
        //All terms are now URIs
264
        //Setup dsftMap - URI to datasetFieldType map
265
        populateFieldTypeMap(metadataBlockSvc);
×
266

267

268
        //Another map - from datasetFieldType to an existing field in the dataset
269
        List<DatasetField> dsfl = dsv.getDatasetFields();
×
270
        Map<DatasetFieldType, DatasetField> fieldByTypeMap = new HashMap<DatasetFieldType, DatasetField>();
×
271
        for (DatasetField dsf : dsfl) {
×
272
            if (fieldByTypeMap.containsKey(dsf.getDatasetFieldType())) {
×
273
                // May have multiple values per field, but not multiple fields of one type?
274
                logger.warning("Multiple fields of type " + dsf.getDatasetFieldType().getName());
×
275
            }
276
            fieldByTypeMap.put(dsf.getDatasetFieldType(), dsf);
×
277
        }
×
278
        
279
        TermsOfUseAndAccess terms = dsv.getTermsOfUseAndAccess().copyTermsOfUseAndAccess();
×
280

281
        //Iterate through input json
282
        for (String key : jsonld.keySet()) {
×
283
            //Skip context (shouldn't be present with decontextualize used above)
284
            if (!key.equals("@context")) {
×
285
                if (dsftMap.containsKey(key)) {
×
286
                    //THere's a field type with theis URI
287
                    DatasetFieldType dsft = dsftMap.get(key);
×
288
                    DatasetField dsf = null;
×
289
                    if (fieldByTypeMap.containsKey(dsft)) {
×
290
                        //There's a field of this type
291
                        dsf = fieldByTypeMap.get(dsft);
×
292

293
                        // Todo - normalize object vs. array
294
                        JsonArray valArray = getValues(jsonld.get(key), dsft.isAllowMultiples(), dsft.getName());
×
295
                        logger.fine("Deleting: " + key + " : " + valArray.toString());
×
296
                        DatasetField dsf2 = getReplacementField(dsf, valArray);
×
297
                        if(dsf2 == null) {
×
298
                            //Exact match - remove the field
299
                            dsfl.remove(dsf);
×
300
                        } else {
301
                            //Partial match - some values of a multivalue field match, so keep the remaining values
302
                            dsfl.remove(dsf);
×
303
                            dsfl.add(dsf2);
×
304
                        }
305
                    }
306
                } else {
×
307
                    // Internal/non-metadatablock terms
308
                    boolean found=false;
×
309
                    if (key.equals(JsonLDTerm.schemaOrg("license").getUrl())) {
×
310
                        if(licenseSvc.getByNameOrUri(jsonld.getString(key)) != null) {
×
311
                            setSemTerm(terms, key, licenseSvc.getDefault());
×
312
                        } else {
313
                            throw new BadRequestException(
×
314
                                    "Term: " + key + " with value: " + jsonld.getString(key) + " not found.");
×
315
                        }
316
                        found=true;
×
317
                    } else if (datasetTerms.contains(key)) {
×
318
                        if(!deleteIfSemTermMatches(terms, key, jsonld.get(key))) {
×
319
                            throw new BadRequestException(
×
320
                                    "Term: " + key + " with value: " + jsonld.getString(key) + " not found.");
×
321
                        }
322
                        found=true;
×
323
                    } else if (key.equals(JsonLDTerm.fileTermsOfAccess.getUrl())) {
×
324
                        JsonObject fAccessObject = jsonld.getJsonObject(JsonLDTerm.fileTermsOfAccess.getUrl());
×
325
                        for (String fileKey : fAccessObject.keySet()) {
×
326
                            if (datafileTerms.contains(fileKey)) {
×
327
                                if(!deleteIfSemTermMatches(terms, key, jsonld.get(key))) {
×
328
                                    throw new BadRequestException(
×
329
                                            "Term: " + key + " with value: " + jsonld.getString(key) + " not found.");
×
330
                                }
331
                                found=true;
×
332
                            }
333
                        }
×
334
                    } else if(!found) {
×
335
                        throw new BadRequestException(
×
336
                                "Term: " + key + " not found.");
337
                                    }
338
                    
339
                    dsv.setTermsOfUseAndAccess(terms);
×
340
                }
341
            }
342
        }
×
343
        dsv.setDatasetFields(dsfl);
×
344
        return dsv;
×
345
    }
346
    
347
    /**
348
     * 
349
     * @param dsf
350
     * @param valArray
351
     * @return null if exact match, otherwise return a field without the value to be deleted
352
     */
353
    private static DatasetField getReplacementField(DatasetField dsf, JsonArray valArray) {
354
        // TODO Parse valArray and remove any matching entries in the dsf
355
        // Until then, delete removes all values of a multivalued field
356
        // Doing this on a required field will fail.
357
        return null;
×
358
    }
359

360
    private static void addField(DatasetField dsf, JsonArray valArray, DatasetFieldType dsft,
361
            DatasetFieldServiceBean datasetFieldSvc, boolean append) {
362

363
        if (append && !dsft.isAllowMultiples()) {
×
364
            if ((dsft.isCompound() && !dsf.getDatasetFieldCompoundValues().isEmpty())
×
365
                    || (dsft.isAllowControlledVocabulary() && !dsf.getControlledVocabularyValues().isEmpty())
×
366
                    || !dsf.getDatasetFieldValues().isEmpty()) {
×
367
                throw new BadRequestException(
×
368
                        "Can't append to a single-value field that already has a value: " + dsft.getName());
×
369
            }
370
        }
371
        logger.fine("Name: " + dsft.getName());
×
372
        logger.fine("val: " + valArray.toString());
×
373
        logger.fine("Compound: " + dsft.isCompound());
×
374
        logger.fine("CV: " + dsft.isAllowControlledVocabulary());
×
375

376
        Map<Long, JsonObject> cvocMap = datasetFieldSvc.getCVocConf(true);
×
377
        
378
        if (dsft.isCompound()) {
×
379
            /*
380
             * List<DatasetFieldCompoundValue> vals = parseCompoundValue(type,
381
             * jsonld.get(key),testType); for (DatasetFieldCompoundValue dsfcv : vals) {
382
             * dsfcv.setParentDatasetField(ret); } dsf.setDatasetFieldCompoundValues(vals);
383
             */
384
            List<DatasetFieldCompoundValue> cvList = dsf.getDatasetFieldCompoundValues();
×
385
            if (!cvList.isEmpty()) {
×
386
                if (!append) {
×
387
                    cvList.clear();
×
388
                } else if (!dsft.isAllowMultiples() && cvList.size() == 1) {
×
389
                    // Trying to append but only a single value is allowed (and there already is
390
                    // one)
391
                    // (and we don't currently support appending new fields within a compound value)
392
                    throw new BadRequestException(
×
393
                            "Append with compound field with single value not yet supported: " + dsft.getDisplayName());
×
394
                }
395
            }
396

397
            List<DatasetFieldCompoundValue> vals = new LinkedList<>();
×
398
            for (JsonValue val : valArray) {
×
399
                if (!(val instanceof JsonObject)) {
×
400
                    throw new BadRequestException(
×
401
                            "Compound field values must be JSON objects, field: " + dsft.getName());
×
402
                }
403
                DatasetFieldCompoundValue cv = null;
×
404

405
                cv = new DatasetFieldCompoundValue();
×
406
                cv.setDisplayOrder(cvList.size());
×
407
                cvList.add(cv);
×
408
                cv.setParentDatasetField(dsf);
×
409

410
                JsonObject obj = (JsonObject) val;
×
411
                for (String childKey : obj.keySet()) {
×
412
                    if (dsftMap.containsKey(childKey)) {
×
413
                        DatasetFieldType childft = dsftMap.get(childKey);
×
414
                        if (!dsft.getChildDatasetFieldTypes().contains(childft)) {
×
415
                            throw new BadRequestException(
×
416
                                    "Compound field " + dsft.getName() + "can't include term " + childKey);
×
417
                        }
418
                        DatasetField childDsf = new DatasetField();
×
419
                        cv.getChildDatasetFields().add(childDsf);
×
420
                        childDsf.setDatasetFieldType(childft);
×
421
                        childDsf.setParentDatasetFieldCompoundValue(cv);
×
422

423
                        JsonArray childValArray = getValues(obj.get(childKey), childft.isAllowMultiples(),
×
424
                                childft.getName());
×
425
                        addField(childDsf, childValArray, childft, datasetFieldSvc, append);
×
426
                    }
427
                }
×
428
            }
×
429

430
        } else if (dsft.isControlledVocabulary()) {
×
431

432
            List<ControlledVocabularyValue> vals = dsf.getControlledVocabularyValues();
×
433
            for (JsonString strVal : valArray.getValuesAs(JsonString.class)) {
×
434
                String strValue = strVal.getString();
×
435
                ControlledVocabularyValue cvv = datasetFieldSvc
×
436
                        .findControlledVocabularyValueByDatasetFieldTypeAndStrValue(dsft, strValue, true);
×
437
                if (cvv == null) {
×
438
                    throw new BadRequestException(
×
439
                            "Unknown value for Controlled Vocab Field: " + dsft.getName() + " : " + strValue);
×
440
                }
441
                // Only add value to the list if it is not a duplicate
442
                if (strValue.equals("Other")) {
×
443
                    logger.fine("vals = " + vals + ", contains: " + vals.contains(cvv));
×
444
                }
445
                if (!vals.contains(cvv)) {
×
446
                    if (vals.size() > 0) {
×
447
                        cvv.setDisplayOrder(vals.size());
×
448
                    }
449
                    vals.add(cvv);
×
450
                    cvv.setDatasetFieldType(dsft);
×
451
                }
452
            }
×
453
            dsf.setControlledVocabularyValues(vals);
×
454

455
        } else {
×
456

457
            boolean extVocab=false;
×
458
            if(cvocMap.containsKey(dsft.getId())) {
×
459
                extVocab=true;
×
460
            }
461
            List<DatasetFieldValue> vals = dsf.getDatasetFieldValues();
×
462

463
            for (JsonString strVal : valArray.getValuesAs(JsonString.class)) {
×
464
                String strValue = strVal.getString();
×
465
                if(extVocab) {
×
466
                    if(!datasetFieldSvc.isValidCVocValue(dsft, strValue)) {
×
467
                        throw new BadRequestException("Invalid values submitted for " + dsft.getName() + " which is limited to specific vocabularies.");
×
468
                    }
469
                }
UNCOV
470
                DatasetFieldValue datasetFieldValue = new DatasetFieldValue();
×
471

472
                datasetFieldValue.setDisplayOrder(vals.size());
×
473
                datasetFieldValue.setValue(strValue.trim());
×
474
                vals.add(datasetFieldValue);
×
475
                datasetFieldValue.setDatasetField(dsf);
×
476

477
            }
×
478
            dsf.setDatasetFieldValues(vals);
×
479
        }
480
    }
×
481

482
    private static JsonArray getValues(JsonValue val, boolean allowMultiples, String name) {
483
        JsonArray valArray = null;
×
484
        if (val instanceof JsonArray) {
×
485
            if ((((JsonArray) val).size() > 1) && !allowMultiples) {
×
486
                throw new BadRequestException("Array for single value notsupported: " + name);
×
487
            } else {
488
                valArray = (JsonArray) val;
×
489
            }
490
        } else {
491
            valArray = Json.createArrayBuilder().add(val).build();
×
492
        }
493
        return valArray;
×
494
    }
495

496
    static Map<String, String> localContext = new TreeMap<String, String>();
×
497
    static Map<String, DatasetFieldType> dsftMap = new TreeMap<String, DatasetFieldType>();
×
498

499
    //A map if DatasetFieldTypes by decontextualized URL
500
    private static void populateFieldTypeMap(MetadataBlockServiceBean metadataBlockSvc) {
501
        if (dsftMap.isEmpty()) {
×
502
            List<MetadataBlock> mdbList = metadataBlockSvc.listMetadataBlocks();
×
503
            for (MetadataBlock mdb : mdbList) {
×
504
                for (DatasetFieldType dsft : mdb.getDatasetFieldTypes()) {
×
505
                    dsftMap.put(dsft.getJsonLDTerm().getUrl(), dsft);
×
506
                }
×
507
            }
×
508
            logger.fine("DSFT Map: " + String.join(", ", dsftMap.keySet()));
×
509
        }
510
    }
×
511

512
    public static void populateContext(MetadataBlockServiceBean metadataBlockSvc) {
513
        if (localContext.isEmpty()) {
×
514

515
            List<MetadataBlock> mdbList = metadataBlockSvc.listMetadataBlocks();
×
516
            for (MetadataBlock mdb : mdbList) {
×
517
                //Assures the mdb's namespace is in the list checked by JsonLDNamespace.isInNamespace() below
518
                mdb.getJsonLDNamespace();
×
519
                for (DatasetFieldType dsft : mdb.getDatasetFieldTypes()) {
×
520
                    if ((dsft.getUri() != null) && !JsonLDNamespace.isInNamespace(dsft.getUri())) {
×
521
                        // Add term if uri exists and it's not in one of the namespaces already defined
522
                        localContext.putIfAbsent(dsft.getName(), dsft.getUri());
×
523
                    }
524
                }
×
525
            }
×
526
            JsonLDNamespace.addNamespacesToContext(localContext);
×
527
            logger.fine("LocalContext keys: " + String.join(", ", localContext.keySet()));
×
528
        }
529
    }
×
530

531
    public static JsonObject decontextualizeJsonLD(String jsonLDString) {
532
        logger.fine(jsonLDString);
×
533
        try (StringReader rdr = new StringReader(jsonLDString)) {
×
534

535
            // Use JsonLd to expand/compact to localContext
536
            try (JsonReader jsonReader = Json.createReader(rdr)) {
×
537
                JsonObject jsonld = jsonReader.readObject();
×
538
                JsonDocument doc = JsonDocument.of(jsonld);
×
539
                JsonArray array = JsonLd.expand(doc).get();
×
540
                jsonld = JsonLd.compact(JsonDocument.of(array), JsonDocument.of(Json.createObjectBuilder().build())).get();
×
541
                // jsonld = array.getJsonObject(0);
542
                logger.fine("Decontextualized object: " + jsonld);
×
543
                return jsonld;
×
544
            } catch (JsonLdError e) {
×
545
                System.out.println(e.getMessage());
×
546
                return null;
×
547
            }
548
        }
×
549
    }
550

551
    private static JsonObject recontextualizeJsonLD(JsonObject jsonldObj, MetadataBlockServiceBean metadataBlockSvc) {
552

553
        populateContext(metadataBlockSvc);
×
554

555
        // Use JsonLd to expand/compact to localContext
556
        JsonDocument doc = JsonDocument.of(jsonldObj);
×
557
        JsonArray array = null;
×
558
        try {
559
            array = JsonLd.expand(doc).get();
×
560

561
            jsonldObj = JsonLd.compact(JsonDocument.of(array), JsonDocument.of(JSONLDUtil.getContext(localContext)))
×
562
                    .get();
×
563
            logger.fine("Compacted: " + jsonldObj.toString());
×
564
            return jsonldObj;
×
565
        } catch (JsonLdError e) {
×
566
            System.out.println(e.getMessage());
×
567
            return null;
×
568
        }
569
    }
570

571
    public static String prettyPrint(JsonValue val) {
572
        StringWriter sw = new StringWriter();
×
573
        Map<String, Object> properties = new HashMap<>(1);
×
574
        properties.put(JsonGenerator.PRETTY_PRINTING, true);
×
575
        JsonWriterFactory writerFactory = Json.createWriterFactory(properties);
×
576
        JsonWriter jsonWriter = writerFactory.createWriter(sw);
×
577
        jsonWriter.write(val);
×
578
        jsonWriter.close();
×
579
        return sw.toString();
×
580
    }
581

582
//Modified from https://stackoverflow.com/questions/3389348/parse-any-date-in-java
583

584
    private static final Map<String, String> DATE_FORMAT_REGEXPS = new HashMap<String, String>() {
×
585
        {
586
            put("^\\d{8}$", "yyyyMMdd");
×
587
            put("^\\d{1,2}-\\d{1,2}-\\d{4}$", "dd-MM-yyyy");
×
588
            put("^\\d{4}-\\d{1,2}-\\d{1,2}$", "yyyy-MM-dd");
×
589
            put("^\\d{1,2}/\\d{1,2}/\\d{4}$", "MM/dd/yyyy");
×
590
            put("^\\d{4}/\\d{1,2}/\\d{1,2}$", "yyyy/MM/dd");
×
591
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}$", "dd MMM yyyy");
×
592
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}$", "dd MMMM yyyy");
×
593
        }
×
594
    };
595

596
    private static final Map<String, String> DATETIME_FORMAT_REGEXPS = new HashMap<String, String>() {
×
597
        {
598
            put("^\\d{12}$", "yyyyMMddHHmm");
×
599
            put("^\\d{8}\\s\\d{4}$", "yyyyMMdd HHmm");
×
600
            put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}$", "dd-MM-yyyy HH:mm");
×
601
            put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy-MM-dd HH:mm");
×
602
            put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}$", "MM/dd/yyyy HH:mm");
×
603
            put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}$", "yyyy/MM/dd HH:mm");
×
604
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMM yyyy HH:mm");
×
605
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}$", "dd MMMM yyyy HH:mm");
×
606
            put("^\\d{14}$", "yyyyMMddHHmmss");
×
607
            put("^\\d{8}\\s\\d{6}$", "yyyyMMdd HHmmss");
×
608
            put("^\\d{1,2}-\\d{1,2}-\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd-MM-yyyy HH:mm:ss");
×
609
            put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy-MM-dd HH:mm:ss");
×
610
            put("^\\d{1,2}/\\d{1,2}/\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "MM/dd/yyyy HH:mm:ss");
×
611
            put("^\\d{4}/\\d{1,2}/\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}$", "yyyy/MM/dd HH:mm:ss");
×
612
            put("^\\d{1,2}\\s[a-z]{3}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMM yyyy HH:mm:ss");
×
613
            put("^\\d{1,2}\\s[a-z]{4,}\\s\\d{4}\\s\\d{1,2}:\\d{2}:\\d{2}$", "dd MMMM yyyy HH:mm:ss");
×
614
            put("^\\d{4}-\\d{1,2}-\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}\\.\\d{3}$", "yyyy-MM-dd HH:mm:ss.SSS");
×
615
            put("^[a-z,A-Z]{3}\\s[a-z,A-Z]{3}\\s\\d{1,2}\\s\\d{1,2}:\\d{2}:\\d{2}\\s[a-z,A-Z]{3}\\s\\d{4}$",
×
616
                    "EEE MMM dd HH:mm:ss zzz yyyy"); // Wed Sep 23 19:33:46 UTC 2020
617

618
        }
×
619
    };
620

621
    /**
622
     * Determine DateTimeFormatter pattern matching with the given date string.
623
     * Returns null if format is unknown. You can simply extend DateUtil with more
624
     * formats if needed.
625
     * 
626
     * @param dateString The date string to determine the SimpleDateFormat pattern
627
     *                   for.
628
     * @return The matching SimpleDateFormat pattern, or null if format is unknown.
629
     * @see SimpleDateFormat
630
     */
631
    public static DateTimeFormatter determineDateTimeFormat(String dateString) {
632
        for (String regexp : DATETIME_FORMAT_REGEXPS.keySet()) {
×
633
            if (dateString.toLowerCase().matches(regexp)) {
×
634
                return DateTimeFormatter.ofPattern(DATETIME_FORMAT_REGEXPS.get(regexp));
×
635
            }
636
        }
×
637
        logger.warning("Unknown datetime format: " + dateString);
×
638
        return null; // Unknown format.
×
639
    }
640

641
    public static DateTimeFormatter determineDateFormat(String dateString) {
642
        for (String regexp : DATE_FORMAT_REGEXPS.keySet()) {
×
643
            if (dateString.toLowerCase().matches(regexp)) {
×
644
                return DateTimeFormatter.ofPattern(DATE_FORMAT_REGEXPS.get(regexp));
×
645
            }
646
        }
×
647
        logger.warning("Unknown date format: " + dateString);
×
648
        return null; // Unknown format.
×
649
    }
650

651
    public static LocalDateTime getDateTimeFrom(String dateString) {
652
        DateTimeFormatter dtf = determineDateTimeFormat(dateString);
×
653
        if (dtf != null) {
×
654
            return LocalDateTime.parse(dateString, dtf);
×
655
        } else {
656
            dtf = determineDateFormat(dateString);
×
657
            if (dtf != null) {
×
658
                return LocalDate.parse(dateString, dtf).atStartOfDay();
×
659
            }
660
        }
661

662
        return null;
×
663
    }
664

665
    // Convenience methods for TermsOfUseAndAccess
666

667
    public static final List<String> datasetTerms = new ArrayList<String>(Arrays.asList(
×
668
            "http://schema.org/license",
669
            "https://dataverse.org/schema/core#termsOfUse",
670
            "https://dataverse.org/schema/core#confidentialityDeclaration",
671
            "https://dataverse.org/schema/core#specialPermissions", "https://dataverse.org/schema/core#restrictions",
672
            "https://dataverse.org/schema/core#citationRequirements",
673
            "https://dataverse.org/schema/core#depositorRequirements", "https://dataverse.org/schema/core#conditions",
674
            "https://dataverse.org/schema/core#disclaimer"));
675
    public static final List<String> datafileTerms = new ArrayList<String>(Arrays.asList(
×
676
            "https://dataverse.org/schema/core#termsOfAccess", "https://dataverse.org/schema/core#fileRequestAccess",
677
            "https://dataverse.org/schema/core#dataAccessPlace", "https://dataverse.org/schema/core#originalArchive",
678
            "https://dataverse.org/schema/core#availabilityStatus",
679
            "https://dataverse.org/schema/core#contactForAccess", "https://dataverse.org/schema/core#sizeOfCollection",
680
            "https://dataverse.org/schema/core#studyCompletion"));
681

682
    public static boolean isSet(TermsOfUseAndAccess terms, String semterm) {
683
        switch (semterm) {
×
684
        case "http://schema.org/license":
685
            return terms.getLicense() != null;
×
686
        case "https://dataverse.org/schema/core#termsOfUse":
687
            return !StringUtils.isBlank(terms.getTermsOfUse());
×
688
        case "https://dataverse.org/schema/core#confidentialityDeclaration":
689
            return !StringUtils.isBlank(terms.getConfidentialityDeclaration());
×
690
        case "https://dataverse.org/schema/core#specialPermissions":
691
            return !StringUtils.isBlank(terms.getSpecialPermissions());
×
692
        case "https://dataverse.org/schema/core#restrictions":
693
            return !StringUtils.isBlank(terms.getRestrictions());
×
694
        case "https://dataverse.org/schema/core#citationRequirements":
695
            return !StringUtils.isBlank(terms.getCitationRequirements());
×
696
        case "https://dataverse.org/schema/core#depositorRequirements":
697
            return !StringUtils.isBlank(terms.getDepositorRequirements());
×
698
        case "https://dataverse.org/schema/core#conditions":
699
            return !StringUtils.isBlank(terms.getConditions());
×
700
        case "https://dataverse.org/schema/core#disclaimer":
701
            return !StringUtils.isBlank(terms.getDisclaimer());
×
702
        case "https://dataverse.org/schema/core#termsOfAccess":
703
            return !StringUtils.isBlank(terms.getTermsOfAccess());
×
704
        case "https://dataverse.org/schema/core#fileRequestAccess":
705
            return !terms.isFileAccessRequest();
×
706
        case "https://dataverse.org/schema/core#dataAccessPlace":
707
            return !StringUtils.isBlank(terms.getDataAccessPlace());
×
708
        case "https://dataverse.org/schema/core#originalArchive":
709
            return !StringUtils.isBlank(terms.getOriginalArchive());
×
710
        case "https://dataverse.org/schema/core#availabilityStatus":
711
            return !StringUtils.isBlank(terms.getAvailabilityStatus());
×
712
        case "https://dataverse.org/schema/core#contactForAccess":
713
            return !StringUtils.isBlank(terms.getContactForAccess());
×
714
        case "https://dataverse.org/schema/core#sizeOfCollection":
715
            return !StringUtils.isBlank(terms.getSizeOfCollection());
×
716
        case "https://dataverse.org/schema/core#studyCompletion":
717
            return !StringUtils.isBlank(terms.getStudyCompletion());
×
718
        default:
719
            logger.warning("isSet called for " + semterm);
×
720
            return false;
×
721
        }
722
    }
723

724
    public static void setSemTerm(TermsOfUseAndAccess terms, String semterm, Object value) {
725
        switch (semterm) {
×
726
        case "http://schema.org/license":
727
            terms.setLicense((License) value);
×
728
            break;
×
729
        case "https://dataverse.org/schema/core#termsOfUse":
730
            terms.setTermsOfUse((String) value);
×
731
            break;
×
732
        case "https://dataverse.org/schema/core#confidentialityDeclaration":
733
            terms.setConfidentialityDeclaration((String) value);
×
734
            break;
×
735
        case "https://dataverse.org/schema/core#specialPermissions":
736
            terms.setSpecialPermissions((String) value);
×
737
            break;
×
738
        case "https://dataverse.org/schema/core#restrictions":
739
            terms.setRestrictions((String) value);
×
740
            break;
×
741
        case "https://dataverse.org/schema/core#citationRequirements":
742
            terms.setCitationRequirements((String) value);
×
743
            break;
×
744
        case "https://dataverse.org/schema/core#depositorRequirements":
745
            terms.setDepositorRequirements((String) value);
×
746
            break;
×
747
        case "https://dataverse.org/schema/core#conditions":
748
            terms.setConditions((String) value);
×
749
            break;
×
750
        case "https://dataverse.org/schema/core#disclaimer":
751
            terms.setDisclaimer((String) value);
×
752
            break;
×
753
        case "https://dataverse.org/schema/core#termsOfAccess":
754
            terms.setTermsOfAccess((String) value);
×
755
            break;
×
756
        case "https://dataverse.org/schema/core#fileRequestAccess":
757
            terms.setFileAccessRequest((boolean) value);
×
758
            break;
×
759
        case "https://dataverse.org/schema/core#dataAccessPlace":
760
            terms.setDataAccessPlace((String) value);
×
761
            break;
×
762
        case "https://dataverse.org/schema/core#originalArchive":
763
            terms.setOriginalArchive((String) value);
×
764
            break;
×
765
        case "https://dataverse.org/schema/core#availabilityStatus":
766
            terms.setAvailabilityStatus((String) value);
×
767
            break;
×
768
        case "https://dataverse.org/schema/core#contactForAccess":
769
            terms.setContactForAccess((String) value);
×
770
            break;
×
771
        case "https://dataverse.org/schema/core#sizeOfCollection":
772
            terms.setSizeOfCollection((String) value);
×
773
            break;
×
774
        case "https://dataverse.org/schema/core#studyCompletion":
775
            terms.setStudyCompletion((String) value);
×
776
            break;
×
777
        default:
778
            logger.warning("setSemTerm called for " + semterm);
×
779
            break;
780
        }
781
    }
×
782

783
    private static boolean deleteIfSemTermMatches(TermsOfUseAndAccess terms, String semterm, JsonValue jsonValue) {
784
        boolean foundTerm=false;
×
785
        String val = null;
×
786
        if(jsonValue.getValueType().equals(ValueType.STRING)) {
×
787
            val = ((JsonString)jsonValue).getString();
×
788
        }
789
        switch (semterm) {
×
790
        
791
        case "https://dataverse.org/schema/core#termsOfUse":
792
            if(terms.getTermsOfUse().equals(val)) {
×
793
                terms.setTermsOfUse(null);
×
794
                foundTerm=true;
×
795
            }
796
            break;
797
        case "https://dataverse.org/schema/core#confidentialityDeclaration":
798
            if(terms.getConfidentialityDeclaration().equals(val)) {
×
799
                terms.setConfidentialityDeclaration(null);
×
800
                foundTerm=true;
×
801
            }
802
            break;
803
        case "https://dataverse.org/schema/core#specialPermissions":
804
            if(terms.getSpecialPermissions().equals(val)) {
×
805
                terms.setSpecialPermissions(null);
×
806
                foundTerm=true;
×
807
            }
808
            break;
809
        case "https://dataverse.org/schema/core#restrictions":
810
            if(terms.getRestrictions().equals(val)) {
×
811
                terms.setRestrictions(null);
×
812
                foundTerm=true;
×
813
            }
814
            break;
815
        case "https://dataverse.org/schema/core#citationRequirements":
816
            if(terms.getCitationRequirements().equals(val)) {
×
817
                terms.setCitationRequirements(null);
×
818
                foundTerm=true;
×
819
            }
820
            break;
821
        case "https://dataverse.org/schema/core#depositorRequirements":
822
            if(terms.getDepositorRequirements().equals(val)) {
×
823
                terms.setDepositorRequirements(null);
×
824
                foundTerm=true;
×
825
            }
826
            break;
827
        case "https://dataverse.org/schema/core#conditions":
828
            if(terms.getConditions().equals(val)) {
×
829
                terms.setConditions(null);
×
830
                foundTerm=true;
×
831
            }
832
            break;
833
        case "https://dataverse.org/schema/core#disclaimer":
834
            if(terms.getDisclaimer().equals(val)) {
×
835
                terms.setDisclaimer(null);
×
836
                foundTerm=true;
×
837
            }
838
            break;
839
        case "https://dataverse.org/schema/core#termsOfAccess":
840
            if(terms.getTermsOfAccess().equals(val)) {
×
841
                terms.setTermsOfAccess(null);
×
842
                foundTerm=true;
×
843
            }
844
            break;
845
        case "https://dataverse.org/schema/core#fileRequestAccess":
846
            if(terms.isFileAccessRequest() && (jsonValue.equals(JsonValue.TRUE))) {
×
847
                terms.setFileAccessRequest(false);
×
848
                foundTerm=true;
×
849
            }
850
            break;
851
        case "https://dataverse.org/schema/core#dataAccessPlace":
852
            if(terms.getDataAccessPlace().equals(val)) {
×
853
                terms.setDataAccessPlace(null);
×
854
                foundTerm=true;
×
855
            }
856
            break;
857
        case "https://dataverse.org/schema/core#originalArchive":
858
            if(terms.getOriginalArchive().equals(val)) {
×
859
                terms.setOriginalArchive(null);
×
860
                foundTerm=true;
×
861
            }
862
            break;
863
        case "https://dataverse.org/schema/core#availabilityStatus":
864
            if(terms.getAvailabilityStatus().equals(val)) {
×
865
                terms.setAvailabilityStatus(null);
×
866
                foundTerm=true;
×
867
            }
868
            break;
869
        case "https://dataverse.org/schema/core#contactForAccess":
870
            if(terms.getContactForAccess().equals(val)) {
×
871
                terms.setContactForAccess(null);
×
872
                foundTerm=true;
×
873
            }
874
            break;
875
        case "https://dataverse.org/schema/core#sizeOfCollection":
876
            if(terms.getSizeOfCollection().equals(val)) {
×
877
                terms.setSizeOfCollection(null);
×
878
                foundTerm=true;
×
879
            }
880
            break;
881
        case "https://dataverse.org/schema/core#studyCompletion":
882
            if(terms.getStudyCompletion().equals(val)) {
×
883
                terms.setStudyCompletion(null);
×
884
                foundTerm=true;
×
885
            }
886
            break;
887
        default:
888
            logger.warning("deleteIfSemTermMatches called for " + semterm);
×
889
            break;
890
        }
891
        return foundTerm; 
×
892
    }
893

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

© 2025 Coveralls, Inc