Skip to content

Commit 7905f12

Browse files
authored
Merge pull request #268 from kit-data-manager/265-store-provenance-in-crates-about-the-tool-which-created-it
265 store provenance in crates about the tool which created it
2 parents bd8aba4 + ec9e9da commit 7905f12

22 files changed

+968
-15
lines changed

CITATION.cff

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ authors:
1010
given-names: "Sabrine"
1111
orcid: "https://orcid.org/0000-0002-4480-6116"
1212
title: "ro-crate-java"
13-
version: 1.0.3
13+
version: 2.1.0-rc3
1414
date-released: 2022-07-19
1515
url: "https://github.com/kit-data-manager/ro-crate-java"

build.gradle

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ configurations {
111111
performanceTestImplementation.extendsFrom implementation
112112
}
113113

114+
// Task for creating a resource file with the version info
115+
tasks.register("generateVersionProps", WriteProperties) { t ->
116+
def generatedResourcesDir = project.layout.buildDirectory.dir(["resources", "main"].join(File.separator))
117+
def outputFile = generatedResourcesDir.map { it.file("version.properties") }
118+
119+
t.destinationFile = outputFile.get().asFile
120+
t.property("version", version)
121+
}
122+
123+
tasks.register("generateVersionPropsTest", WriteProperties) { t ->
124+
def generatedResourcesDir = project.layout.buildDirectory.dir(["resources", "test"].join(File.separator))
125+
def outputFile = generatedResourcesDir.map { it.file("version.properties") }
126+
127+
t.destinationFile = outputFile.get().asFile
128+
t.property("version", version)
129+
}
114130

115131
tasks.register('performanceContextEntitiesBenchmark', JavaExec) {
116132
description = "Run the context entities benchmarks."
@@ -154,8 +170,13 @@ tasks.register('performanceReadWriteMultipleCratesBenchmark', JavaExec) {
154170
mainClass = 'edu.kit.datamanager.ro_crate.multiplecrates.MultipleCratesWriteAndRead'
155171
}
156172

173+
compileJava {
174+
dependsOn generateVersionProps
175+
}
176+
157177
test {
158178
useJUnitPlatform()
179+
dependsOn generateVersionPropsTest
159180
finalizedBy jacocoTestReport
160181
}
161182

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@
2222
*/
2323
public interface Crate {
2424

25+
/**
26+
* Mark the crate as imported, i.e. it has been read from a file
27+
* or is for other reasons not considered a new crate.
28+
* <p>
29+
* This is useful mostly for readers to indicate this in case
30+
* the crate may not have any provenance information yet and
31+
* should still be recognized as an imported crate.
32+
*
33+
* @return this crate, for convenience.
34+
*/
35+
Crate markAsImported();
36+
37+
/**
38+
* Check if the crate is marked as imported.
39+
* <p>
40+
* If true, it indicates that the crate has been read from a file
41+
* or is for other reasons not considered a new crate.
42+
*
43+
* @return true if the crate is marked as imported, false otherwise.
44+
*/
45+
boolean isImported();
46+
2547
/**
2648
* Read version from the crate descriptor and return it as a class
2749
* representation.

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ public class RoCrate implements Crate {
5050

5151
protected Collection<File> untrackedFiles;
5252

53+
/**
54+
* Indicates whether this crate has been imported from an external source.
55+
* This is used to determine if ro-crate-java should add a CreateAction
56+
* or an UpdateAction in the provenance on export.
57+
*/
58+
protected boolean isImported = false;
59+
60+
@Override
61+
public RoCrate markAsImported() {
62+
this.isImported = true;
63+
return this;
64+
}
65+
66+
@Override
67+
public boolean isImported() {
68+
return this.isImported;
69+
}
70+
5371
@Override
5472
public CratePreview getPreview() {
5573
return this.roCratePreview;

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

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,19 @@ public JsonNode getProperty(String propertyKey) {
112112
return this.properties.get(propertyKey);
113113
}
114114

115+
/**
116+
* Returns the value of the property with the given key as a String.
117+
* If the property is not found, it returns null.
118+
*
119+
* @param propertyKey the key of the property.
120+
* @return the value of the property as a String or null if not found.
121+
*/
122+
public String getIdProperty(String propertyKey) {
123+
return Optional.ofNullable(this.properties.get(propertyKey))
124+
.map(jsonNode -> jsonNode.path("@id").asText(null))
125+
.orElse(null);
126+
}
127+
115128
@JsonIgnore
116129
public String getId() {
117130
JsonNode id = this.properties.get("@id");
@@ -241,9 +254,7 @@ private static boolean addProperty(ObjectNode whereToAdd, String key, JsonNode v
241254
public void addIdProperty(String name, String id) {
242255
if (id == null || id.isBlank()) { return; }
243256
mergeIdIntoValue(id, this.properties.get(name))
244-
.ifPresent(newValue -> {
245-
this.properties.set(name, newValue);
246-
});
257+
.ifPresent(newValue -> this.properties.set(name, newValue));
247258
this.linkedTo.add(id);
248259
this.notifyObservers();
249260
}
@@ -356,7 +367,7 @@ private static void checkFormatISO8601(String date) throws IllegalArgumentExcept
356367
/**
357368
* Adds a property with date time format. The property should match the ISO 8601
358369
* date format.
359-
*
370+
* <p>
360371
* Same as {@link #addProperty(String, String)} but with internal check.
361372
*
362373
* @param key key of the property (e.g. datePublished)
@@ -411,7 +422,7 @@ public T setId(String id) {
411422
if (IdentifierUtils.isValidUri(id)) {
412423
this.id = id;
413424
} else {
414-
this.id = IdentifierUtils.encode(id).get();
425+
this.id = IdentifierUtils.encode(id).orElse(this.id);
415426
}
416427
}
417428
return self();
@@ -448,7 +459,7 @@ public T addTypes(Collection<String> types) {
448459
/**
449460
* Adds a property with date time format. The property should match the ISO 8601
450461
* date format.
451-
*
462+
* <p>
452463
* Same as {@link #addProperty(String, String)} but with internal check.
453464
*
454465
* @param key key of the property (e.g. datePublished)
@@ -508,7 +519,7 @@ public T addProperty(String key, boolean value) {
508519
/**
509520
* ID properties are often used when referencing other entities within
510521
* the ROCrate. This method adds automatically such one.
511-
*
522+
* <p>
512523
* Instead of {@code "name": "id" }
513524
* this will add {@code "name" : {"@id": "id"} }
514525
*

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ default void generate(Crate crate, File targetDir) throws IOException {
4040
// as this is usually called in the process of writing a crate
4141
// (including preview)
4242
new CrateWriter<>(new WriteFolderStrategy().disablePreview())
43+
// We assume the caller (e.g. a writer) already stored the provenance.
44+
.withAutomaticProvenance(null)
4345
.save(crate, targetDir.getAbsolutePath());
4446
this.saveAllToFolder(targetDir);
4547
try (var stream = Files.list(targetDir.toPath())) {

src/main/java/edu/kit/datamanager/ro_crate/reader/CrateReader.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public RoCrate readCrate(T location) throws IOException {
9797
usedFiles.add(files.toPath().resolve(FILE_METADATA_JSON).toFile().getPath());
9898
usedFiles.add(files.toPath().resolve(FILE_PREVIEW_HTML).toFile().getPath());
9999
usedFiles.add(files.toPath().resolve(FILE_PREVIEW_FILES).toFile().getPath());
100-
return rebuildCrate(metadataJson, files, usedFiles);
100+
return rebuildCrate(metadataJson, files, usedFiles).markAsImported();
101101
}
102102

103103
private RoCrate rebuildCrate(ObjectNode metadataJson, File files, HashSet<String> usedFiles) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package edu.kit.datamanager.ro_crate.util;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.net.URL;
6+
import java.util.Properties;
7+
8+
public class ClasspathPropertiesVersionProvider implements VersionProvider {
9+
public static final String VERSION_PROPERTIES = "version.properties";
10+
11+
/**
12+
* Cached version to avoid repeated file/resource reads.
13+
*/
14+
private String cachedVersion = null;
15+
16+
/**
17+
* Constructs a ClasspathPropertiesVersionProvider that reads the version from a properties file in the classpath.
18+
*/
19+
public ClasspathPropertiesVersionProvider() {
20+
this.cachedVersion = getVersion();
21+
}
22+
23+
@Override
24+
public String getVersion() {
25+
if (cachedVersion != null) {
26+
return cachedVersion;
27+
}
28+
29+
URL resource = this.getClass().getResource("/" + VERSION_PROPERTIES);
30+
assert resource != null : VERSION_PROPERTIES + " not found in classpath";
31+
32+
try (InputStream input = resource.openStream()) {
33+
Properties properties = new Properties();
34+
properties.load(input);
35+
String version = properties.getProperty("version");
36+
assert version != null : "Version property not found in " + VERSION_PROPERTIES;
37+
return version.trim();
38+
} catch (IOException e) {
39+
throw new IllegalStateException("Failed to read version from properties file", e);
40+
}
41+
}
42+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package edu.kit.datamanager.ro_crate.util;
2+
3+
import com.fasterxml.jackson.databind.JsonNode;
4+
5+
import java.util.stream.StreamSupport;
6+
7+
/**
8+
* Utility class for handling operations on RO-Crate graphs.
9+
* Provides methods to find entities by ID or type within a graph.
10+
* <p>
11+
* {@see JsonUtilFunctions}.
12+
*/
13+
public class Graph {
14+
15+
private Graph() {
16+
// Private constructor to prevent instantiation
17+
}
18+
19+
/**
20+
* Finds an entity in the graph by its ID.
21+
*
22+
* @param graph The JSON node representing the graph.
23+
* @param id The ID of the entity to find.
24+
* @return The entity as a JsonNode if found, null otherwise.
25+
*/
26+
public static JsonNode findEntityById(JsonNode graph, String id) {
27+
for (JsonNode entity : graph) {
28+
if (entity.has("@id") && entity.get("@id").asText().equals(id)) {
29+
return entity;
30+
}
31+
}
32+
return null;
33+
}
34+
35+
/**
36+
* Finds an entity in the graph by its type.
37+
*
38+
* @param graph The JSON node representing the graph.
39+
* @param type The type of the entity to find.
40+
* @return The entity as a JsonNode if found, null otherwise.
41+
*/
42+
public static JsonNode findEntityByType(JsonNode graph, String type) {
43+
return StreamSupport.stream(graph.spliterator(), false)
44+
.filter(entity -> entity.path("@type").asText().equals(type))
45+
.findFirst()
46+
.orElse(null);
47+
}
48+
49+
/**
50+
* Finds all entities in the graph by their type.
51+
*
52+
* @param graph The JSON node representing the graph.
53+
* @param type The type of the entities to find.
54+
* @return An array of JsonNode containing all entities of the specified type.
55+
*/
56+
public static JsonNode[] findEntitiesByType(JsonNode graph, String type) {
57+
return StreamSupport.stream(graph.spliterator(), false)
58+
.filter(entity -> entity.path("@type").asText().equals(type))
59+
.toArray(JsonNode[]::new);
60+
}
61+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package edu.kit.datamanager.ro_crate.util;
2+
3+
public interface VersionProvider {
4+
/**
5+
* Returns the version of the ro-crate-java library.
6+
*
7+
* @return The version string.
8+
*/
9+
String getVersion();
10+
}

0 commit comments

Comments
 (0)