1 package org.simantics.databoard.container;
3 import java.io.DataInput;
4 import java.io.DataInputStream;
5 import java.io.DataOutput;
7 import java.io.FileInputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
11 import java.util.TreeMap;
13 import org.simantics.databoard.Bindings;
14 import org.simantics.databoard.binding.Binding;
15 import org.simantics.databoard.binding.impl.TreeMapBinding;
16 import org.simantics.databoard.binding.mutable.Variant;
17 import org.simantics.databoard.serialization.Serializer;
18 import org.simantics.databoard.type.Datatype;
19 import org.simantics.databoard.util.binary.BinaryFile;
22 * An utility class for reading files whose format follows
23 * {@link DataContainer}.
24 * @author Hannu Niemistö
26 public class DataContainers {
28 private static final Serializer STRING_SERIALIZER =
29 Bindings.getSerializerUnchecked(Bindings.STRING);
30 private static final Serializer INTEGER_SERIALIZER =
31 Bindings.getSerializerUnchecked(Bindings.INTEGER);
32 private static final Serializer METADATA_SERIALIZER =
33 Bindings.getSerializerUnchecked(new TreeMapBinding(Bindings.STRING, Bindings.VARIANT));
34 private static final Serializer DATATYPE_SERIALIZER =
35 Bindings.getSerializerUnchecked(Datatype.class);
36 private static final Serializer VARIANT_SERIALIZER =
37 Bindings.getSerializerUnchecked(Bindings.VARIANT);
38 private static final Serializer DATA_CONTAINER_SERIALIZER =
39 Bindings.getSerializerUnchecked(DataContainer.class);
42 * Binds a format name and a version together to form a format version
49 public static String toFormatString(String formatName, int version) {
50 return formatName + ":" + version;
54 * Reads only the header of the data container. Returns
55 * a DataContainer whose content-field is null.
58 public static DataContainer readHeader(DataInput input) throws IOException {
59 String format = (String)STRING_SERIALIZER.deserialize(input);
60 int version = (Integer)INTEGER_SERIALIZER.deserialize(input);
61 @SuppressWarnings("unchecked")
62 TreeMap<String,Variant> metadata = (TreeMap<String,Variant>)METADATA_SERIALIZER.deserialize(input);
63 return new DataContainer(format, version, metadata, null);
67 * Consumes a header from a given stream and checks that the header satisfies the given format and version.
68 * Returns the obtained header if the check fails and null on success.
71 public static DataContainer requireHeader(DataInput input, String format, int version) {
73 DataContainer header = readHeader(input);
74 if(!format.equals(header.format) || version != header.version)
77 } catch (Throwable t) {
78 return new DataContainer("unknown", 0, null, null);
83 * Consumes a header from a given stream and checks that the header satisfies the given format and version restrictions.
84 * Returns the obtained header if the check fails and null on success.
87 public static DataContainer requireHeader(DataInput input, String... requiredFormatStrings) {
89 DataContainer header = readHeader(input);
90 String formatString = toFormatString(header.format, header.version);
91 for (String requiredFormatString : requiredFormatStrings) {
92 if(formatString.equals(requiredFormatString))
96 } catch (Throwable t) {
97 return new DataContainer("unknown", 0, null, null);
102 * Checks that the given file satisfies the given format and version.
103 * Returns the obtained header if the check fails and null on success.
104 * @throws IOException
106 public static DataContainer validateHeader(File file, String format, int version) throws IOException {
107 try (InputStream stream = new FileInputStream( file )) {
108 return DataContainers.requireHeader(new DataInputStream(stream), format, version);
113 * Checks that the given file satisfies the given format and version.
114 * Returns the obtained header if the check fails and null on success.
115 * @throws IOException
117 public static DataContainer validateHeader(File file, String... allowedFormatStrings) throws IOException {
118 try (InputStream stream = new FileInputStream( file )) {
119 return DataContainers.requireHeader(new DataInputStream(stream), allowedFormatStrings);
124 * Reads only the header of the data container. Returns
125 * a DataContainer whose content-field is null.
126 * @throws IOException
128 public static DataContainer readHeader(File input) throws IOException {
129 try (BinaryFile rf = new BinaryFile( input, "r" )) {
130 return readHeader(rf);
135 * Reads a data container including the content data.
136 * @throws IOException
138 public static DataContainer readFile(DataInput input) throws IOException {
139 DataContainer result = readHeader(input);
140 result.content = (Variant)VARIANT_SERIALIZER.deserialize(input);
145 * Reads a data container including the content data.
146 * @throws IOException
148 public static DataContainer readFile(DataInput input, Binding expectedBinding) throws IOException, DataFormatException {
149 DataContainer result = readHeader(input);
150 Datatype contentType = (Datatype) DATATYPE_SERIALIZER.deserialize(input);
151 if (!expectedBinding.type().equals(contentType))
152 throw new DataFormatException(
153 "Content type didn't match the type expected for the binding " + expectedBinding
154 + ":\nexpected type: " + expectedBinding.type()
155 + "\nactual type: " + contentType);
156 Object value = Bindings.getSerializerUnchecked(expectedBinding).deserialize(input);
157 result.content = new Variant(expectedBinding, value);
162 * Process a data container using a format handler matching the format and version
164 * @param handlers Map of handlers. Keys are strings of form "format:version".
166 public static <T> T readFile(DataInput input, Map<String, FormatHandler<T>> handlers) throws Exception {
167 DataContainer result = readHeader(input);
169 FormatHandler<T> handler = handlers.get(result.format + ":" + result.version);
171 throw new DataFormatException("Unknown data format " + result.format + " version " + result.version + ".");
172 Binding binding = handler.getBinding();
174 Datatype contentType = (Datatype)DATATYPE_SERIALIZER.deserialize(input);
175 if(!binding.type().equals(contentType))
176 throw new DataFormatException("Content type didn't match the type expected for the format " + result.format + " version " + result.version + ".");
178 Object value = Bindings.getSerializerUnchecked(binding).deserialize(input);
180 result.content = new Variant(binding, value);
181 return handler.process(result);
185 * Reads a data container including the content data.
186 * @throws IOException
188 public static DataContainer readFile(File input) throws IOException {
189 try (BinaryFile rf = new BinaryFile( input, "r" )) {
195 * Reads a data container including the content data.
196 * @throws IOException
197 * @throws DataFormatException
199 public static DataContainer readFile(File input, Binding expectedBinding) throws IOException, DataFormatException {
200 try (BinaryFile rf = new BinaryFile( input, "r" )) {
201 return readFile(rf, expectedBinding);
206 * Process a data container using a format handler matching the format and version
208 * @param handlers Map of handlers. Keys are strings of form "format:version".
210 public static <T> T readFile(File input, Map<String, FormatHandler<T>> handlers) throws Exception {
211 try (BinaryFile rf = new BinaryFile( input, "r" )) {
212 return readFile(rf, handlers);
217 * Writes header fields of a container to the given output. Content field is
219 * @throws IOException
221 public static void writeHeader(DataOutput output, DataContainer container) throws IOException {
222 STRING_SERIALIZER.serialize(output, container.format);
223 INTEGER_SERIALIZER.serialize(output, container.version);
224 METADATA_SERIALIZER.serialize(output, container.metadata);
228 * Writes a data container to the given output.
229 * @throws IOException
231 public static void writeFile(DataOutput output, DataContainer container) throws IOException {
232 writeHeader(output, container);
233 VARIANT_SERIALIZER.serialize(output, container.content);
237 * Writes a data container to the given file
238 * @throws IOException
240 public static void writeFile(File output, DataContainer container) throws IOException {
241 try (BinaryFile wf = new BinaryFile(output)) {
242 writeFile(wf, container);
243 wf.setLength(wf.position());
247 public static byte[] writeFile(DataContainer container) throws IOException {
248 return DATA_CONTAINER_SERIALIZER.serialize(container);