Skip to content

Commit 810d199

Browse files
authored
Merge pull request #256 from kit-data-manager/make-spec-examples-executable
Make specification examples (from readme) executable
2 parents f1a1a72 + 61fd863 commit 810d199

13 files changed

Lines changed: 1219 additions & 713 deletions

File tree

.github/workflows/gradle.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ on:
1111
push:
1212
branches: [ main, development ]
1313
pull_request:
14-
branches: [ main, development ]
1514
workflow_dispatch:
1615

1716
env:

README.md

Lines changed: 19 additions & 672 deletions
Large diffs are not rendered by default.

src/main/java/edu/kit/datamanager/ro_crate/RoCrate.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,10 @@
1010
import edu.kit.datamanager.ro_crate.entities.AbstractEntity;
1111
import edu.kit.datamanager.ro_crate.entities.contextual.ContextualEntity;
1212
import edu.kit.datamanager.ro_crate.entities.contextual.JsonDescriptor;
13-
import edu.kit.datamanager.ro_crate.entities.contextual.OrganizationEntity;
1413
import edu.kit.datamanager.ro_crate.entities.data.DataEntity;
15-
import edu.kit.datamanager.ro_crate.entities.data.DataEntity.DataEntityBuilder;
16-
import edu.kit.datamanager.ro_crate.entities.data.FileEntity;
14+
1715
import edu.kit.datamanager.ro_crate.entities.data.RootDataEntity;
1816
import edu.kit.datamanager.ro_crate.externalproviders.dataentities.ImportFromDataCite;
19-
import edu.kit.datamanager.ro_crate.externalproviders.organizationprovider.RorProvider;
2017
import edu.kit.datamanager.ro_crate.objectmapper.MyObjectMapper;
2118
import edu.kit.datamanager.ro_crate.payload.CratePayload;
2219
import edu.kit.datamanager.ro_crate.payload.RoCratePayload;
@@ -26,12 +23,9 @@
2623
import edu.kit.datamanager.ro_crate.special.JsonUtilFunctions;
2724
import edu.kit.datamanager.ro_crate.validation.JsonSchemaValidation;
2825
import edu.kit.datamanager.ro_crate.validation.Validator;
29-
import edu.kit.datamanager.ro_crate.writer.FolderWriter;
30-
import edu.kit.datamanager.ro_crate.writer.RoCrateWriter;
3126

3227
import java.io.File;
3328
import java.net.URI;
34-
import java.nio.file.Paths;
3529
import java.util.*;
3630
import java.util.stream.Collectors;
3731
import java.util.stream.StreamSupport;
@@ -354,6 +348,18 @@ public RoCrateBuilder addName(String name) {
354348
return this;
355349
}
356350

351+
/**
352+
* Adds an "identifier" property to the root data entity.
353+
* <p>
354+
* This is useful e.g. to assign e.g. a DOI to this crate.
355+
* @param identifier the identifier to add.
356+
* @return this builder.
357+
*/
358+
public RoCrateBuilder addIdentifier(String identifier) {
359+
this.rootDataEntity.addProperty("identifier", identifier.strip());
360+
return this;
361+
}
362+
357363
public RoCrateBuilder addDescription(String description) {
358364
this.rootDataEntity.addProperty(PROPERTY_DESCRIPTION, description);
359365
return this;

src/main/java/edu/kit/datamanager/ro_crate/entities/AbstractEntity.java

Lines changed: 61 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -239,33 +239,60 @@ private static boolean addProperty(ObjectNode whereToAdd, String key, JsonNode v
239239
* @param id the "id" of the property.
240240
*/
241241
public void addIdProperty(String name, String id) {
242-
JsonNode jsonNode = addToIdProperty(name, id, this.properties.get(name));
243-
if (jsonNode != null) {
244-
this.linkedTo.add(id);
245-
this.properties.set(name, jsonNode);
246-
this.notifyObservers();
247-
}
242+
mergeIdIntoValue(id, this.properties.get(name))
243+
.ifPresent(newValue -> {
244+
this.linkedTo.add(id);
245+
this.properties.set(name, newValue);
246+
this.notifyObservers();
247+
});
248248
}
249249

250-
private static JsonNode addToIdProperty(String name, String id, JsonNode property) {
251-
ObjectMapper objectMapper = MyObjectMapper.getMapper();
252-
if (name != null && id != null) {
253-
if (property == null) {
254-
return objectMapper.createObjectNode().put("@id", id);
255-
} else {
256-
if (property.isArray()) {
257-
ArrayNode ns = (ArrayNode) property;
258-
ns.add(objectMapper.createObjectNode().put("@id", id));
259-
return ns;
260-
} else {
261-
ArrayNode newNodes = objectMapper.createArrayNode();
262-
newNodes.add(property);
263-
newNodes.add(objectMapper.createObjectNode().put("@id", id));
264-
return newNodes;
265-
}
266-
}
250+
/**
251+
* Merges the given id into the current value,
252+
* using this representation: {"@id" : "id"}.
253+
* <p>
254+
* The current value can be null without errors.
255+
* Only the id will be considered in this case.
256+
* <p>
257+
* If the id is null-ish, it will not be added, similar to a null-ish value.
258+
* If the id is already present, nothing will be done.
259+
* If it is not an array and the id is not present, an array will be applied.
260+
*
261+
* @param id the id to add.
262+
* @param currentValue the current value of the property.
263+
* @return The updated value of the property.
264+
* Empty if value does not change!
265+
*/
266+
private static Optional<JsonNode> mergeIdIntoValue(String id, JsonNode currentValue) {
267+
if (id == null || id.isBlank()) { return Optional.empty(); }
268+
269+
ObjectMapper jsonBuilder = MyObjectMapper.getMapper();
270+
ObjectNode newIdObject = jsonBuilder.createObjectNode().put("@id", id);
271+
if (currentValue == null || currentValue.isNull() || currentValue.isMissingNode()) {
272+
return Optional.ofNullable(newIdObject);
273+
}
274+
275+
boolean isIdAlready = currentValue.asText().equals(id);
276+
boolean isIdObjectAlready = currentValue.path("@id").asText().equals(id);
277+
boolean isArrayWithIdPresent = currentValue.valueStream()
278+
.anyMatch(node -> node
279+
.path("@id")
280+
.asText()
281+
.equals(id));
282+
if (isIdAlready || isIdObjectAlready || isArrayWithIdPresent) {
283+
return Optional.empty();
284+
}
285+
286+
if (currentValue.isArray() && currentValue instanceof ArrayNode currentValueAsArray) {
287+
currentValueAsArray.add(newIdObject);
288+
return Optional.of(currentValueAsArray);
289+
} else {
290+
// property is not an array, so we make it an array
291+
ArrayNode newNodes = jsonBuilder.createArrayNode();
292+
newNodes.add(currentValue);
293+
newNodes.add(newIdObject);
294+
return Optional.of(newNodes);
267295
}
268-
return null;
269296
}
270297

271298
/**
@@ -369,6 +396,11 @@ protected String getId() {
369396
/**
370397
* Setting the id property of the entity, if the given value is not
371398
* null. If the id is not encoded, the encoding will be done.
399+
* <p>
400+
* <b>NOTE: IDs are not just names!</b> The ID may have effects
401+
* on parts of your crate! For example: If the entity represents a
402+
* file which will be copied into the crate, writers must use the
403+
* ID as filename.
372404
*
373405
* @param id the String representing the id.
374406
* @return the generic builder.
@@ -486,11 +518,11 @@ public T addProperty(String key, boolean value) {
486518
* @return the generic builder
487519
*/
488520
public T addIdProperty(String name, String id) {
489-
JsonNode jsonNode = AbstractEntity.addToIdProperty(name, id, this.properties.get(name));
490-
if (jsonNode != null) {
491-
this.properties.set(name, jsonNode);
492-
this.relatedItems.add(id);
493-
}
521+
AbstractEntity.mergeIdIntoValue(id, this.properties.get(name))
522+
.ifPresent(newValue -> {
523+
this.properties.set(name, newValue);
524+
this.relatedItems.add(id);
525+
});
494526
return self();
495527
}
496528

src/main/java/edu/kit/datamanager/ro_crate/preview/StaticPreview.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import edu.kit.datamanager.ro_crate.util.ZipUtil;
44
import java.io.File;
55
import java.io.IOException;
6-
import java.util.Optional;
76

87
import net.lingala.zip4j.ZipFile;
98
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
@@ -13,7 +12,7 @@
1312
/**
1413
* This class adds a static preview to the crate, which consists of a
1514
* metadataHtml file and a folder containing other files required to render
16-
* metadataHtml. If will be put unchanged to the writer output, i.e., a zip
15+
* metadataHtml. It will be put unchanged to the writer output, i.e., a zip
1716
* file, folder, or stream.
1817
*
1918
* @author jejkal

src/test/java/edu/kit/datamanager/ro_crate/HelpFunctions.java

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import com.fasterxml.jackson.databind.JsonNode;
55
import com.fasterxml.jackson.databind.ObjectMapper;
66

7+
import com.fasterxml.jackson.databind.SerializationFeature;
78
import edu.kit.datamanager.ro_crate.entities.AbstractEntity;
89
import edu.kit.datamanager.ro_crate.objectmapper.MyObjectMapper;
910
import edu.kit.datamanager.ro_crate.special.JsonUtilFunctions;
1011

1112
import org.apache.commons.io.FileUtils;
1213
import io.json.compare.JSONCompare;
1314
import io.json.compare.JsonComparator;
15+
import org.opentest4j.AssertionFailedError;
1416

1517
import java.io.File;
1618
import java.io.IOException;
@@ -34,15 +36,15 @@ public static void compareEntityWithFile(AbstractEntity entity, String string) t
3436

3537
public static void compare(JsonNode node1, JsonNode node2, Boolean equals) {
3638
var comparator = new JsonComparator() {
37-
public boolean compareValues(Object expected, Object actual) {
38-
39+
public boolean compareValues(Object expected, Object actual) {
3940
return expected.equals(actual);
4041
}
4142

4243
public boolean compareFields(String expected, String actual) {
4344
return expected.equals(actual);
4445
}
4546
};
47+
4648
if (equals) {
4749
JSONCompare.assertMatches(node1, node2, comparator);
4850
} else {
@@ -73,13 +75,46 @@ public static void compareTwoCrateJson(Crate crate1, Crate crate2) throws JsonPr
7375
compare(node1, node2, true);
7476
}
7577

78+
public static void prettyPrintJsonString(String minimalJsonMetadata) {
79+
try {
80+
ObjectMapper objectMapper = new ObjectMapper();
81+
JsonNode jsonNode = objectMapper.readTree(minimalJsonMetadata);
82+
// Enable pretty printing
83+
String prettyJson = objectMapper
84+
.enable(SerializationFeature.INDENT_OUTPUT)
85+
.writeValueAsString(jsonNode);
86+
// Print the pretty JSON
87+
System.out.println(prettyJson);
88+
} catch (JsonProcessingException e) {
89+
throw new AssertionFailedError("Not able to process string as JSON!", e);
90+
}
91+
}
92+
93+
public static void printAndAssertEquals(RoCrate crate, String pathToResource) {
94+
// So you get something to see
95+
prettyPrintJsonString(crate.getJsonMetadata());
96+
// Compare with the example from the specification
97+
try {
98+
HelpFunctions.compareCrateJsonToFileInResources(crate, pathToResource);
99+
} catch (IOException e) {
100+
throw new AssertionFailedError("Missing resources file!", e);
101+
}
102+
}
103+
76104
public static void compareCrateJsonToFileInResources(File file1, File file2) throws IOException {
77105
ObjectMapper objectMapper = MyObjectMapper.getMapper();
78106
JsonNode node1 = JsonUtilFunctions.unwrapSingleArray(objectMapper.readTree(file1));
79107
JsonNode node2 = JsonUtilFunctions.unwrapSingleArray(objectMapper.readTree(file2));
80108
compare(node1, node2, true);
81109
}
82110

111+
/**
112+
* Compares the JSON metadata of a Crate object with a JSON file in the resources directory.
113+
*
114+
* @param crate1 The Crate object to compare.
115+
* @param jsonFileString The path to the JSON file in the resources directory.
116+
* @throws IOException If an error occurs while reading the JSON file.
117+
*/
83118
public static void compareCrateJsonToFileInResources(Crate crate1, String jsonFileString) throws IOException {
84119
InputStream inputStream = HelpFunctions.class.getResourceAsStream(
85120
jsonFileString);

0 commit comments

Comments
 (0)