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

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

10 Apr 2025 09:06AM UTC coverage: 85.3%. First build
#334

Pull #233

github

ThomasJejkal
Fixed issues with stale writers
Pull Request #233: Version 2.1.0

280 of 440 new or added lines in 15 files covered. (63.64%)

1764 of 2068 relevant lines covered (85.3%)

0.85 hits per line

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

57.14
/src/main/java/edu/kit/datamanager/ro_crate/preview/CustomPreview.java
1
package edu.kit.datamanager.ro_crate.preview;
2

3
import com.fasterxml.jackson.databind.JsonNode;
4
import com.fasterxml.jackson.databind.ObjectMapper;
5
import edu.kit.datamanager.ro_crate.preview.model.ROCratePreviewModel;
6
import edu.kit.datamanager.ro_crate.util.ZipUtil;
7
import freemarker.template.Configuration;
8
import freemarker.template.Template;
9
import freemarker.template.TemplateException;
10
import freemarker.template.TemplateExceptionHandler;
11
import java.io.File;
12
import java.io.FileOutputStream;
13
import java.io.FileWriter;
14
import java.io.IOException;
15
import java.io.OutputStreamWriter;
16
import java.io.Writer;
17
import java.util.ArrayList;
18
import java.util.Collections;
19
import java.util.HashMap;
20
import java.util.LinkedList;
21
import java.util.List;
22
import java.util.Map;
23
import net.lingala.zip4j.ZipFile;
24
import net.lingala.zip4j.exception.ZipException;
25
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
26
import org.apache.commons.io.FileUtils;
27

28
/**
29
 * This class generates a custom preview without requiring external
30
 * dependencies, i.e., rochtml. Therefore, the FreeMarker template located under
31
 * resources/templates/custom_preview.ftl is used.
32
 *
33
 * @author jejkal
34
 */
35
public class CustomPreview implements CratePreview {
36

37
    private final Configuration cfg;
38

39
    public CustomPreview() {
1✔
40
        cfg = new Configuration(Configuration.VERSION_2_3_34);
1✔
41
        cfg.setClassForTemplateLoading(CustomPreview.class, "/");
1✔
42
        cfg.setDefaultEncoding("UTF-8");
1✔
43
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
1✔
44

45
    }
1✔
46

47
    public ROCratePreviewModel mapFromJson(String metadata) throws IOException {
48
        ObjectMapper mapper = new ObjectMapper();
1✔
49
        JsonNode root = (JsonNode) mapper.readValue(metadata, JsonNode.class);
1✔
50
        JsonNode graph = root.get("@graph");
1✔
51
        ROCratePreviewModel.ROCrate crate = new ROCratePreviewModel.ROCrate();
1✔
52
        List<ROCratePreviewModel.Dataset> datasets = new ArrayList<>();
1✔
53
        List<ROCratePreviewModel.File> files = new ArrayList<>();
1✔
54

55
        if (graph.isArray()) {
1✔
56

57
            for (JsonNode node : graph) {
1✔
58
                String id = node.get("@id").asText();
1✔
59
                List<String> types = new LinkedList<>();
1✔
60
                if (node.get("@type").isArray()) {
1✔
61

NEW
62
                    Collections.addAll(types, (String[]) mapper.convertValue(node.get("@type"), String[].class));
×
63
                } else {
64
                    types.add(node.get("@type").asText());
1✔
65
                }
66

67
                if (types.contains("Dataset") && "./".equals(id)) {
1✔
68
                    crate.name = node.get("name").asText();
1✔
69
                    crate.description = node.get("description") == null ? null : node.get("description").asText();
1✔
70
                    crate.type = "Dataset";
1✔
71
                    if (node.get("license") != null) {
1✔
72
                        crate.license = node.get("license").isObject() ? node.get("license").get("@id").asText() : node.get("license").asText();
1✔
73
                    }
74
                    crate.datePublished = node.get("datePublished") == null ? null : node.get("datePublished").asText();
1✔
75
                    crate.hasPart = new ArrayList<>();
1✔
76

77
                    if (node.has("hasPart")) {
1✔
NEW
78
                        for (JsonNode part : node.get("hasPart")) {
×
NEW
79
                            ROCratePreviewModel.Part p = new ROCratePreviewModel.Part();
×
NEW
80
                            p.id = part.get("@id").asText();
×
NEW
81
                            p.name = part.get("@id").asText(); // Name will be replaced later
×
NEW
82
                            crate.hasPart.add(p);
×
NEW
83
                        }
×
84

85
                    }
86
                } else if (types.contains("Dataset")) {
1✔
NEW
87
                    ROCratePreviewModel.Dataset dataset = new ROCratePreviewModel.Dataset();
×
NEW
88
                    dataset.id = id;
×
NEW
89
                    dataset.name = node.get("name").asText();
×
NEW
90
                    dataset.description = node.get("description").asText();
×
NEW
91
                    datasets.add(dataset);
×
92
                } else if (types.contains("File")) {
1✔
NEW
93
                    ROCratePreviewModel.File file = new ROCratePreviewModel.File();
×
NEW
94
                    file.id = id;
×
NEW
95
                    file.name = node.get("name") == null ? null : node.get("name").asText();
×
NEW
96
                    file.description = node.get("description") == null ? null : node.get("description").asText();
×
NEW
97
                    file.contentSize = node.get("contentSize") == null ? null : node.get("contentSize").asText();
×
NEW
98
                    file.encodingFormat = node.get("encodingFormat") == null ? null : node.get("encodingFormat").asText();
×
NEW
99
                    files.add(file);
×
100
                }
101
            }
1✔
102
        }
103

104
        // Update Part names using dataset and file lists
105
        if (crate.hasPart != null) {
1✔
106
            for (ROCratePreviewModel.Part part : crate.hasPart) {
1✔
NEW
107
                for (ROCratePreviewModel.Dataset dataset : datasets) {
×
NEW
108
                    if (dataset.id.equals(part.id) && dataset.name != null) {
×
NEW
109
                        part.name = dataset.name;
×
110
                    }
NEW
111
                }
×
NEW
112
                for (ROCratePreviewModel.File file : files) {
×
NEW
113
                    if (file.id.equals(part.id) && file.name != null) {
×
NEW
114
                        part.name = file.name;
×
115
                    }
NEW
116
                }
×
NEW
117
            }
×
118
        }
119

120
        ROCratePreviewModel model = new ROCratePreviewModel();
1✔
121
        model.crate = crate;
1✔
122
        model.datasets = datasets;
1✔
123
        model.files = files;
1✔
124
        return model;
1✔
125
    }
126

127
    @Override
128
    public void saveAllToZip(ZipFile zipFile) throws IOException {
129
        if (zipFile == null) {
1✔
NEW
130
            throw new IOException("Argument zipFile must not be null.");
×
131
        }
132
        try {
133
            zipFile.extractFile("ro-crate-metadata.json", "temp");
1✔
134
        } catch (ZipException ex) {
1✔
135
            throw new IOException("ro-crate-metadata.json not found in provided ZIP.", ex);
1✔
136
        }
1✔
137

138
        String metadata = FileUtils.readFileToString(new File("temp/ro-crate-metadata.json"), "UTF-8");
1✔
139
        try {
140
            Map<String, Object> dataModel = new HashMap<>();
1✔
141
            dataModel.put("crateModel", mapFromJson(metadata));
1✔
142
            Template temp = cfg.getTemplate("templates/custom_preview.ftl");
1✔
143
            try (Writer out = new OutputStreamWriter(new FileOutputStream("temp/ro-crate-preview.html"))) {
1✔
144
                temp.process(dataModel, out);
1✔
145
                out.flush();
1✔
146
            }
147
            zipFile.addFile("temp/ro-crate-preview.html");
1✔
NEW
148
        } catch (TemplateException ex) {
×
NEW
149
            throw new IOException("Failed to generate preview.", ex);
×
150
        } finally {
151
            try {
152
                FileUtils.deleteDirectory(new File("temp"));
1✔
NEW
153
            } catch (IOException ex) {
×
154
                //ignore
155
            }
1✔
156
        }
157
    }
1✔
158

159
    @Override
160
    public void saveAllToFolder(File folder) throws IOException {
161
        if (folder == null || !folder.exists()) {
1✔
162
            throw new IOException("Preview target folder " + folder + " does not exist.");
1✔
163
        }
164
        String metadata = FileUtils.readFileToString(new File(folder, "ro-crate-metadata.json"), "UTF-8");
1✔
165
        try {
166
            Map<String, Object> dataModel = new HashMap<>();
1✔
167
            dataModel.put("crateModel", mapFromJson(metadata));
1✔
168
            Template temp = cfg.getTemplate("templates/custom_preview.ftl");
1✔
169
            try (Writer out = new OutputStreamWriter(new FileOutputStream(new File(folder, "ro-crate-preview.html")))) {
1✔
170
                temp.process(dataModel, out);
1✔
171
                out.flush();
1✔
172
            }
NEW
173
        } catch (TemplateException ex) {
×
NEW
174
            throw new IOException("Failed to generate preview.", ex);
×
175
        }
1✔
176
    }
1✔
177

178
    @Override
179
    public void saveAllToStream(String metadata, ZipOutputStream stream) throws IOException {
180
        try {
181
            //prepare metadata for template
NEW
182
            Map<String, Object> dataModel = new HashMap<>();
×
NEW
183
            dataModel.put("crateModel", mapFromJson(metadata));
×
184

185
            //prepare output folder and writer
NEW
186
            FileUtils.forceMkdir(new File("temp"));
×
187
            //load and process template
NEW
188
            try (FileWriter writer = new FileWriter(new File("temp/ro-crate-preview.html"))) {
×
189
                //load and process template
NEW
190
                Template temp = cfg.getTemplate("templates/custom_preview.frm");
×
NEW
191
                temp.process(dataModel, writer);
×
NEW
192
                writer.flush();
×
193
            }
194

NEW
195
            ZipUtil.addFileToZipStream(stream, new File("temp/ro-crate-preview.html"), "ro-crate-preview.html");
×
NEW
196
        } catch (TemplateException ex) {
×
NEW
197
            throw new IOException("Failed to generate preview.", ex);
×
198
        } finally {
199
            try {
NEW
200
                FileUtils.deleteDirectory(new File("temp"));
×
NEW
201
            } catch (IOException ex) {
×
202
                //ignore
NEW
203
            }
×
204
        }
205
    }
×
206

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