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