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

kit-data-manager / ro-crate-java / #427

12 May 2025 02:44PM UTC coverage: 89.284% (-0.9%) from 90.169%
#427

Pull #258

github

web-flow
Merge e2a4bf3f1 into 810d1995c
Pull Request #258: Support .ELN-style crates in all zip readers and writers

223 of 244 new or added lines in 14 files covered. (91.39%)

26 existing lines in 5 files now uncovered.

1983 of 2221 relevant lines covered (89.28%)

0.89 hits per line

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

95.0
/src/main/java/edu/kit/datamanager/ro_crate/reader/ZipStrategy.java
1
package edu.kit.datamanager.ro_crate.reader;
2

3
import com.fasterxml.jackson.databind.ObjectMapper;
4
import com.fasterxml.jackson.databind.node.ObjectNode;
5
import edu.kit.datamanager.ro_crate.entities.contextual.JsonDescriptor;
6
import edu.kit.datamanager.ro_crate.objectmapper.MyObjectMapper;
7
import net.lingala.zip4j.ZipFile;
8
import org.apache.commons.io.FileUtils;
9
import org.apache.commons.io.filefilter.FileFilterUtils;
10

11
import java.io.File;
12
import java.io.IOException;
13
import java.nio.file.Path;
14
import java.util.UUID;
15

16
/**
17
 * A ReaderStrategy implementation which reads from ZipFiles.
18
 * <p>
19
 * May be used as a dependency for CrateReader. It will unzip
20
 * the ZipFile in a path relative to the directory this application runs in.
21
 * By default, it will be `./.tmp/ro-crate-java/zipReader/$UUID/`.
22
 * <p>
23
 * NOTE: The resulting crate may refer to these temporary files. Therefore,
24
 * these files are only being deleted before the JVM exits. If you need to free
25
 * space because your application is long-running or creates a lot of
26
 * crates, you may use the getters to retrieve information which will help
27
 * you to clean up manually. Keep in mind that crates may refer to this
28
 * folder after extraction. Use RoCrateWriter to export it so some
29
 * persistent location and possibly read it from there, if required. Or use
30
 * the ZipWriter to write it back to its source.
31
 */
32
public class ZipStrategy implements GenericReaderStrategy<String> {
33

34
  protected final String ID = UUID.randomUUID().toString();
1✔
35
  protected Path temporaryFolder = Path.of(String.format("./.tmp/ro-crate-java/zipReader/%s/", ID));
1✔
36
  protected boolean isExtracted = false;
1✔
37

38
  /**
39
   * Crates a ZipReader with the default configuration as described in the class documentation.
40
   */
41
  public ZipStrategy() {}
1✔
42

43
  /**
44
   * Creates a ZipReader which will extract the contents temporary
45
   * to the given location instead of the default location.
46
   *
47
   * @param folderPath            the custom directory to extract
48
   *                              content to for temporary access.
49
   * @param shallAddUuidSubfolder if true, the reader will extract
50
   *                              into subdirectories of the given
51
   *                              directory. These subdirectories
52
   *                              will have UUIDs as their names.
53
   */
54
  public ZipStrategy(Path folderPath, boolean shallAddUuidSubfolder) {
1✔
55
    if (shallAddUuidSubfolder) {
1✔
56
      this.temporaryFolder = folderPath.resolve(ID);
1✔
57
    } else {
58
      this.temporaryFolder = folderPath;
1✔
59
    }
60
  }
1✔
61

62
  /**
63
   * @return the identifier which may be used as the name for a subfolder in the temporary directory.
64
   */
65
  public String getID() {
66
    return ID;
1✔
67
  }
68

69
  /**
70
   * @return the folder (considered temporary) where the zipped crate will be or has been extracted to.
71
   */
72
  public Path getTemporaryFolder() {
73
    return temporaryFolder;
1✔
74
  }
75

76
  /**
77
   * @return whether the crate has already been extracted into the temporary folder.
78
   */
79
  public boolean isExtracted() {
80
    return isExtracted;
1✔
81
  }
82

83
  private void readCrate(String location) throws IOException {
84
    File folder = temporaryFolder.toFile();
1✔
85
    // ensure the directory is clean
86
    if (folder.isDirectory()) {
1✔
87
      FileUtils.cleanDirectory(folder);
1✔
88
    } else if (folder.isFile()) {
1✔
NEW
89
      FileUtils.delete(folder);
×
90
    }
91
    // extract
92
    try (ZipFile zf = new ZipFile(location)) {
1✔
93
      zf.extractAll(temporaryFolder.toAbsolutePath().toString());
1✔
94
      this.isExtracted = true;
1✔
95
    }
96
    // register deletion on exit
97
    FileUtils.forceDeleteOnExit(folder);
1✔
98
  }
1✔
99

100
  @Override
101
  public ObjectNode readMetadataJson(String location) throws IOException {
102
    if (!isExtracted) {
1✔
103
      this.readCrate(location);
1✔
104
    }
105

106
    ObjectMapper objectMapper = MyObjectMapper.getMapper();
1✔
107
    File jsonMetadata = this.temporaryFolder.resolve(JsonDescriptor.ID).toFile();
1✔
108
    if (!jsonMetadata.isFile()) {
1✔
109
      // Try to find the metadata file in subdirectories
110
      File firstSubdir = FileUtils.listFilesAndDirs(
1✔
111
            temporaryFolder.toFile(),
1✔
112
            FileFilterUtils.directoryFileFilter(),
1✔
113
            null // not recursive
114
        )
115
        .stream()
1✔
116
        .limit(50)
1✔
117
        .filter(file -> file.toPath().toAbsolutePath().resolve(JsonDescriptor.ID).toFile().isFile())
1✔
118
        .findFirst()
1✔
119
        .orElseThrow(() -> new IllegalStateException("No %s found in zip file".formatted(JsonDescriptor.ID)));
1✔
120
      jsonMetadata = firstSubdir.toPath().resolve(JsonDescriptor.ID).toFile();
1✔
121
    }
122

123
    return objectMapper.readTree(jsonMetadata).deepCopy();
1✔
124
  }
125

126
  @Override
127
  public File readContent(String location) throws IOException {
128
    if (!isExtracted) {
1✔
129
      this.readCrate(location);
×
130
    }
131
    return temporaryFolder.toFile();
1✔
132
  }
133
}
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