1 package de.dlr.shepard.neo4Core.export;
2
3 import java.io.ByteArrayInputStream;
4 import java.io.ByteArrayOutputStream;
5 import java.io.File;
6 import java.io.IOException;
7 import java.io.InputStream;
8 import java.time.LocalDateTime;
9 import java.time.ZoneId;
10 import java.time.format.DateTimeFormatter;
11 import java.util.ArrayList;
12 import java.util.Date;
13 import java.util.List;
14 import java.util.zip.ZipEntry;
15 import java.util.zip.ZipOutputStream;
16
17 import com.fasterxml.jackson.databind.ObjectMapper;
18
19 import de.dlr.shepard.neo4Core.entities.Collection;
20 import de.dlr.shepard.neo4Core.entities.DataObject;
21 import de.dlr.shepard.neo4Core.entities.User;
22 import de.dlr.shepard.neo4Core.io.AbstractDataObjectIO;
23 import de.dlr.shepard.neo4Core.io.BasicEntityIO;
24 import de.dlr.shepard.neo4Core.io.BasicReferenceIO;
25 import de.dlr.shepard.neo4Core.io.CollectionIO;
26 import de.dlr.shepard.neo4Core.io.DataObjectIO;
27 import edu.kit.datamanager.ro_crate.RoCrate.RoCrateBuilder;
28 import edu.kit.datamanager.ro_crate.entities.contextual.PersonEntity;
29 import edu.kit.datamanager.ro_crate.entities.data.FileEntity;
30 import edu.kit.datamanager.ro_crate.entities.data.FileEntity.FileEntityBuilder;
31 import lombok.AccessLevel;
32 import lombok.Getter;
33 import lombok.extern.slf4j.Slf4j;
34
35 @Slf4j
36 public class ExportBuilder {
37
38 private ObjectMapper objectMapper = new ObjectMapper();
39
40 @Getter(value = AccessLevel.PROTECTED)
41 private RoCrateBuilder builder;
42 private ByteArrayOutputStream baos;
43 private ZipOutputStream zos;
44 private List<String> entries;
45
46 public ExportBuilder(Collection collection) throws IOException {
47 baos = new ByteArrayOutputStream();
48 zos = new ZipOutputStream(baos);
49 entries = new ArrayList<>();
50
51 var roCrateName = collection.getName() + " Research Object Crate";
52 var roCrateDescription = "Research Object Crate representing the shepard Collection " + collection.getName();
53 builder = new RoCrateBuilder(roCrateName, roCrateDescription);
54
55 var collectionEntity = createFileEntity(new CollectionIO(collection));
56 builder.addDataEntity(collectionEntity);
57 addPersonEntity(collection.getCreatedBy());
58 }
59
60 public ExportBuilder addDataObject(DataObject dataObject) throws IOException {
61 var dataObjectEntity = createFileEntity(new DataObjectIO(dataObject));
62 builder.addDataEntity(dataObjectEntity);
63 addPersonEntity(dataObject.getCreatedBy());
64 return this;
65 }
66
67 public ExportBuilder addReference(BasicReferenceIO reference, User author) throws IOException {
68 var referenceEntity = createFileEntity(reference);
69 builder.addDataEntity(referenceEntity);
70 addPersonEntity(author);
71 return this;
72 }
73
74 public ExportBuilder addPayload(byte[] payload, String filename, String name) throws IOException {
75 addToZip(filename, payload);
76 builder.addDataEntity(createFileEntityBuilder(filename, name).build());
77 return this;
78 }
79
80 public ExportBuilder addPayload(byte[] payload, String filename, String name, String encodingFormat)
81 throws IOException {
82 addToZip(filename, payload);
83 builder.addDataEntity(createFileEntityBuilder(filename, name, encodingFormat).build());
84 return this;
85 }
86
87 private void addPersonEntity(User user) {
88 if (user == null)
89 return;
90
91 var entity = new PersonEntity.PersonEntityBuilder().setId(user.getUsername()).setEmail(user.getEmail())
92 .setGivenName(user.getFirstName()).setFamilyName(user.getLastName()).build();
93
94 builder.addContextualEntity(entity);
95 }
96
97 public InputStream build() throws IOException {
98 var roCrate = builder.build();
99 Object jsonObject = objectMapper.readValue(roCrate.getJsonMetadata(), Object.class);
100 byte[] bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(jsonObject);
101 addToZip(ExportConstants.ROCRATE_METADATA, bytes);
102 zos.close();
103
104 return new ByteArrayInputStream(baos.toByteArray());
105 }
106
107 private FileEntity createFileEntity(AbstractDataObjectIO entity) throws IOException {
108 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
109 writeEntityToZip(filename, entity);
110
111 var fileEntityBuilder = createFileEntityBuilder(entity, filename);
112 fileEntityBuilder.addProperty(ExportConstants.DESCRIPTION_PROP, entity.getDescription());
113 return fileEntityBuilder.build();
114 }
115
116 private FileEntity createFileEntity(BasicReferenceIO entity) throws IOException {
117 var filename = entity.getId() + ExportConstants.JSON_FILE_EXTENSION;
118 writeEntityToZip(filename, entity);
119
120 var fileEntityBuilder = createFileEntityBuilder(entity, filename);
121 fileEntityBuilder.addProperty(ExportConstants.TYPE_PROP, entity.getType());
122 return fileEntityBuilder.build();
123 }
124
125 private void writeEntityToZip(String filename, Object entity) throws IOException {
126 var bytes = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsBytes(entity);
127 addToZip(filename, bytes);
128 }
129
130 private static FileEntityBuilder createFileEntityBuilder(String filename, String name) {
131
132
133
134 var dummyFile = new File("dummy.txt");
135 return new FileEntity.FileEntityBuilder().setId(filename).setSource(dummyFile)
136 .addProperty(ExportConstants.NAME_PROP, name);
137 }
138
139 private static FileEntityBuilder createFileEntityBuilder(String filename, String name, String encodingFormat) {
140 var fileEntityBuilder = createFileEntityBuilder(filename, name);
141 fileEntityBuilder.addProperty("encodingFormat", encodingFormat);
142 return fileEntityBuilder;
143 }
144
145 private static FileEntityBuilder createFileEntityBuilder(BasicEntityIO entity, String filename) {
146 var createdAt = convertToIsoDate(entity.getCreatedAt());
147 var type = entity.getClass().getSimpleName().replace("IO", "");
148
149 var fileEntityBuilder = createFileEntityBuilder(filename, entity.getName(), "application/json");
150 fileEntityBuilder.addAuthor(entity.getCreatedBy()).addProperty(ExportConstants.CREATED_PROP, createdAt)
151 .addProperty(ExportConstants.TYPE_PROP, type);
152
153 if (entity.getUpdatedAt() != null)
154 fileEntityBuilder.addProperty(ExportConstants.UPDATED_PROP, convertToIsoDate(entity.getUpdatedAt()));
155
156 return fileEntityBuilder;
157 }
158
159 private static String convertToIsoDate(Date dateToConvert) {
160 var localDateTime = LocalDateTime.ofInstant(dateToConvert.toInstant(), ZoneId.systemDefault());
161 return DateTimeFormatter.ISO_DATE_TIME.format(localDateTime);
162 }
163
164 private void addToZip(String filename, byte[] bytes) throws IOException {
165 if (entries.contains(filename)) {
166 log.warn("{} already in zip file, skipping", filename);
167 return;
168 }
169
170 zos.putNextEntry(new ZipEntry(filename));
171 zos.write(bytes);
172 zos.closeEntry();
173 entries.add(filename);
174 }
175
176 }