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

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

29 Apr 2025 06:44PM UTC coverage: 89.357%. First build
#392

Pull #233

github

Pfeil
fix: add newline at end of gradle.yml for consistency
Pull Request #233: Version 2.1.0

608 of 700 new or added lines in 28 files covered. (86.86%)

1889 of 2114 relevant lines covered (89.36%)

0.89 hits per line

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

88.33
/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.util.ZipUtil;
6
import freemarker.template.Configuration;
7
import freemarker.template.Template;
8
import freemarker.template.TemplateException;
9
import freemarker.template.TemplateExceptionHandler;
10
import java.io.File;
11
import java.io.FileOutputStream;
12
import java.io.FileWriter;
13
import java.io.IOException;
14
import java.io.OutputStreamWriter;
15
import java.io.Writer;
16
import java.util.ArrayList;
17
import java.util.Collections;
18
import java.util.HashMap;
19
import java.util.LinkedList;
20
import java.util.List;
21
import java.util.Map;
22
import net.lingala.zip4j.ZipFile;
23
import net.lingala.zip4j.exception.ZipException;
24
import net.lingala.zip4j.io.outputstream.ZipOutputStream;
25
import org.apache.commons.io.FileUtils;
26
import org.slf4j.Logger;
27
import org.slf4j.LoggerFactory;
28

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

38
    private final static Logger logger = LoggerFactory.getLogger(CustomPreview.class);
1✔
39

40
    private Template template = null;
1✔
41

42
    public CustomPreview() {
1✔
43
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_34);
1✔
44
        cfg.setClassForTemplateLoading(CustomPreview.class, "/");
1✔
45
        cfg.setDefaultEncoding("UTF-8");
1✔
46
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
1✔
47
        try {
48
            template = cfg.getTemplate("templates/custom_preview.ftl");
1✔
NEW
49
        } catch (IOException ex) {
×
NEW
50
            logger.error("Failed to read template for CustomPreview.", ex);
×
51
        }
1✔
52
    }
1✔
53

54
    private CustomPreviewModel mapFromJson(String metadata) throws IOException {
55
        ObjectMapper mapper = new ObjectMapper();
1✔
56
        JsonNode root = (JsonNode) mapper.readValue(metadata, JsonNode.class);
1✔
57
        JsonNode graph = root.get("@graph");
1✔
58
        CustomPreviewModel.ROCrate crate = new CustomPreviewModel.ROCrate();
1✔
59
        List<CustomPreviewModel.Dataset> datasets = new ArrayList<>();
1✔
60
        List<CustomPreviewModel.File> files = new ArrayList<>();
1✔
61

62
        if (graph.isArray()) {
1✔
63

64
            for (JsonNode node : graph) {
1✔
65
                String id = node.path("@id").asText();
1✔
66
                List<String> types = new LinkedList<>();
1✔
67
                if (node.path("@type").isArray()) {
1✔
68

69
                    Collections.addAll(types, mapper.convertValue(node.path("@type"), String[].class));
1✔
70
                } else {
71
                    types.add(node.path("@type").asText());
1✔
72
                }
73

74
                if (types.contains("Dataset") && "./".equals(id)) {
1✔
75
                    crate.name = node.path("name").asText();
1✔
76
                    crate.description = node.path("description").asText(null);
1✔
77
                    crate.type = "Dataset";
1✔
78
                    crate.license = node.path("license").path("@id").asText(node.path("license").asText(null));
1✔
79
                    crate.datePublished = node.path("datePublished").asText(null);
1✔
80
                    crate.hasPart = new ArrayList<>();
1✔
81

82
                    for (JsonNode part : node.path("hasPart")) {
1✔
83
                        CustomPreviewModel.Part p = new CustomPreviewModel.Part();
1✔
84
                        String tmpId = part.path("@id").asText(part.asText());
1✔
85
                        p.id = tmpId;
1✔
86
                        p.name = tmpId; // Name will be replaced later
1✔
87
                        crate.hasPart.add(p);
1✔
88
                    }
1✔
89
                } else if (types.contains("Dataset")) {
1✔
90
                    CustomPreviewModel.Dataset dataset = new CustomPreviewModel.Dataset();
1✔
91
                    dataset.id = id;
1✔
92
                    dataset.name = node.path("name").asText();
1✔
93
                    dataset.description = node.path("description").asText();
1✔
94
                    datasets.add(dataset);
1✔
95
                } else if (types.contains("File")) {
1✔
96
                    CustomPreviewModel.File file = new CustomPreviewModel.File();
1✔
97
                    file.id = id;
1✔
98
                    file.name = node.path("name").asText(null);
1✔
99
                    file.description = node.path("description").asText(null);
1✔
100
                    file.contentSize = node.path("contentSize").asText(null);
1✔
101
                    file.encodingFormat = node.path("encodingFormat").asText(null);
1✔
102
                    files.add(file);
1✔
103
                }
104
            }
1✔
105
        }
106

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

123
        CustomPreviewModel model = new CustomPreviewModel();
1✔
124
        model.crate = crate;
1✔
125
        model.datasets = datasets;
1✔
126
        model.files = files;
1✔
127
        return model;
1✔
128
    }
129

130
    @Override
131
    public void saveAllToZip(ZipFile zipFile) throws IOException {
132
        if (template == null) {
1✔
NEW
133
            throw new IOException("Preview template did not load. Unable to proceed.");
×
134
        }
135
        if (zipFile == null) {
1✔
NEW
136
            throw new IOException("Argument zipFile must not be null.");
×
137
        }
138
        try {
139
            zipFile.extractFile("ro-crate-metadata.json", "temp");
1✔
140
        } catch (ZipException ex) {
1✔
141
            throw new IOException("ro-crate-metadata.json not found in provided ZIP.", ex);
1✔
142
        }
1✔
143

144
        String metadata = FileUtils.readFileToString(new File("temp/ro-crate-metadata.json"), "UTF-8");
1✔
145
        try {
146
            Map<String, Object> dataModel = new HashMap<>();
1✔
147
            dataModel.put("crateModel", mapFromJson(metadata));
1✔
148

149
            try (Writer out = new OutputStreamWriter(new FileOutputStream("temp/ro-crate-preview.html"))) {
1✔
150
                template.process(dataModel, out);
1✔
151
                out.flush();
1✔
152
            }
153
            zipFile.addFile("temp/ro-crate-preview.html");
1✔
NEW
154
        } catch (TemplateException ex) {
×
NEW
155
            throw new IOException("Failed to generate preview.", ex);
×
156
        } finally {
157
            try {
158
                FileUtils.deleteDirectory(new File("temp"));
1✔
NEW
159
            } catch (IOException ex) {
×
160
                //ignore
161
            }
1✔
162
        }
163
    }
1✔
164

165
    @Override
166
    public void saveAllToFolder(File folder) throws IOException {
167
        if (template == null) {
1✔
NEW
168
            throw new IOException("Preview template did not load. Unable to proceed.");
×
169
        }
170
        if (folder == null || !folder.exists()) {
1✔
171
            throw new IOException("Preview target folder " + folder + " does not exist.");
1✔
172
        }
173
        String metadata = FileUtils.readFileToString(new File(folder, "ro-crate-metadata.json"), "UTF-8");
1✔
174
        try {
175
            Map<String, Object> dataModel = new HashMap<>();
1✔
176
            dataModel.put("crateModel", mapFromJson(metadata));
1✔
177
            try (Writer out = new OutputStreamWriter(new FileOutputStream(new File(folder, "ro-crate-preview.html")))) {
1✔
178
                template.process(dataModel, out);
1✔
179
                out.flush();
1✔
180
            }
NEW
181
        } catch (TemplateException ex) {
×
NEW
182
            throw new IOException("Failed to generate preview.", ex);
×
183
        }
1✔
184
    }
1✔
185

186
    @Override
187
    public void saveAllToStream(String metadata, ZipOutputStream stream) throws IOException {
188
        if (template == null) {
1✔
NEW
189
            throw new IOException("Preview template did not load. Unable to proceed.");
×
190
        }
191
        try {
192
            //prepare metadata for template
193
            Map<String, Object> dataModel = new HashMap<>();
1✔
194
            dataModel.put("crateModel", mapFromJson(metadata));
1✔
195

196
            //prepare output folder and writer
197
            FileUtils.forceMkdir(new File("temp"));
1✔
198
            //load and process template
199
            try (FileWriter writer = new FileWriter(new File("temp/ro-crate-preview.html"))) {
1✔
200
                //load and process template
201
                template.process(dataModel, writer);
1✔
202
                writer.flush();
1✔
203
            }
204

205
            ZipUtil.addFileToZipStream(stream, new File("temp/ro-crate-preview.html"), "ro-crate-preview.html");
1✔
NEW
206
        } catch (TemplateException ex) {
×
NEW
207
            throw new IOException("Failed to generate preview.", ex);
×
208
        } finally {
209
            try {
210
                FileUtils.deleteDirectory(new File("temp"));
1✔
NEW
211
            } catch (IOException ex) {
×
212
                //ignore
213
            }
1✔
214
        }
215
    }
1✔
216

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