1 /*******************************************************************************
\r
2 * Copyright (c) 2010 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.databoard.serialization;
14 import gnu.trove.map.hash.TObjectIntHashMap;
\r
16 import java.io.ByteArrayInputStream;
\r
17 import java.io.DataInput;
\r
18 import java.io.DataOutput;
\r
19 import java.io.File;
\r
20 import java.io.IOException;
\r
21 import java.io.InputStream;
\r
22 import java.io.OutputStream;
\r
23 import java.nio.ByteBuffer;
\r
24 import java.util.ArrayList;
\r
25 import java.util.List;
\r
27 import org.simantics.databoard.Files;
\r
28 import org.simantics.databoard.util.binary.BinaryFile;
\r
29 import org.simantics.databoard.util.binary.BinaryReadable;
\r
30 import org.simantics.databoard.util.binary.ByteBufferReadable;
\r
31 import org.simantics.databoard.util.binary.ByteBufferWriteable;
\r
32 import org.simantics.databoard.util.binary.InputStreamReadable;
\r
33 import org.simantics.databoard.util.binary.OutputStreamWriteable;
\r
36 * Databoard binary serializer.
\r
38 * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
\r
40 public abstract class Serializer {
43 * Serialize obj to out.
45 * The identities argument is a map of identities (in the binary block) and objects
46 * that have already been serialized. Once serialized, an object is added to the map.
47 * Typically an empty map is provided. If the type has no recursion, i.e. Referable
\r
48 * Records, a <code>null</code> value can be provided.
51 * @param identities Thread local empty map or <code>null</code> if there is no recursion
55 public abstract void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException;
56 public abstract void serialize(DataOutput out, Object obj) throws IOException;
57 public void serialize(OutputStream out, Object obj) throws IOException
\r
59 OutputStreamWriteable writ = new OutputStreamWriteable(out);
\r
60 serialize(writ, obj);
\r
64 * Deserialize an object from a readable.
66 * The identities argument is a list of identities (in the binary block) of objects
67 * that have already been deserialized. Once deserialized they are added to the list.
68 * Typically an empty list is provided. If the type has no recursion, i.e. Referable
\r
69 * Records, a <code>null</code> value can be provided.<p>
\r
71 * Note, if in argument is instanceof BinaryReadable or RandomAccessBinary,
\r
72 * the serializer performs extra protection against malformed data when
\r
73 * deserializing arrays and maps. This prevents the serializer from
\r
74 * instanting potentially out-of-memory-invoking huge arrays. For example,
\r
75 * if data data says array size is 0xffffffff (-1), 4GB is allocated ->
\r
76 * out of memory exception -> unhandled runtime error. BinaryReadable has
\r
77 * length limit which allowes serializer to estimate whether future data is
\r
80 * @param in DataInput, BinaryReadable or RandomAccessBinary
81 * @param identities empty identities array or <code>null</code> if there is no recursion
82 * @return the instance
85 public abstract Object deserialize(DataInput in, List<Object> identities) throws IOException;
86 public abstract Object deserialize(DataInput in) throws IOException;
90 * Deserialize into an existing instance. This method writes over previous values.
\r
94 * @param dst valid object
\r
95 * @throws IOException
\r
97 public abstract void deserializeTo(DataInput in, List<Object> identities, Object dst) throws IOException;
\r
98 public abstract void deserializeTo(DataInput in, Object dst) throws IOException;
\r
101 * Attempt deserialize to existing instance. Creates new if not possible.
\r
104 * @param identities
\r
106 * @return dst or new obj
\r
107 * @throws IOException
\r
109 public Object deserializeToTry(DataInput in, List<Object> identities, Object dst) throws IOException
\r
111 deserializeTo(in, identities, dst);
\r
116 * Deserialize the next object in an input stream.
117 * Note, if multiple objects are deserialized from the same stream, it is
118 * more efficient to instantiate InputStreamReadable and identities only once,
\r
119 * and use {@link #deserialize(DataInput, List)}.
122 * @return The object deserialized into a Java Object
123 * @throws IOException
125 public Object deserialize(InputStream in) throws IOException
127 // InputStreamReadable adapts InputStream to BinaryReadable&DataInput
\r
128 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
129 return deserialize(read);
133 * Deserialize from an input stream into an object.
\r
134 * Note, if multiple objects are deserialized from the same stream, it is
\r
135 * more efficient to instantiate InputStreamReadable and identities only once,
\r
136 * and use {@link #deserialize(DataInput, List)}.
\r
139 * @param obj a valid object
\r
140 * @throws IOException
\r
142 public void deserialize(InputStream in, Object obj) throws IOException
\r
144 // InputStreamReadable adapts InputStream to BinaryReadable&DataInput
\r
145 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
\r
146 deserializeTo(read, obj);
\r
150 * Deserialize object from a file.
\r
152 * @param file source file
\r
153 * @return the object
\r
154 * @throws IOException
\r
156 public Object deserialize(File file) throws IOException
\r
158 BinaryFile f = new BinaryFile(file);
\r
160 return deserialize(f);
\r
167 * Deserialize a file into a valid object. This method writes over previous values.
\r
169 * @param file source file
\r
170 * @param obj a dst valid object
\r
171 * @throws IOException
\r
173 public void deserialize(File file, Object obj) throws IOException
\r
175 BinaryFile f = new BinaryFile(file);
\r
177 deserializeTo(f, obj);
\r
184 * Deserialize an object in byte[] format.
187 * @return the instance
188 * @throws IOException
190 public Object deserialize(byte[] data) throws IOException
192 ByteBuffer buffer = ByteBuffer.wrap( data );
193 ByteBufferReadable readable = new ByteBufferReadable( buffer );
194 return deserialize(readable);
198 * Deserialize byte[] into a valid object.
\r
201 * @param obj dst valid object
\r
202 * @throws IOException
\r
204 public void deserialize(byte[] data, Object obj) throws IOException
\r
206 ByteBuffer buffer = ByteBuffer.wrap( data );
\r
207 ByteBufferReadable readable = new ByteBufferReadable( buffer );
\r
208 deserializeTo(readable, obj);
\r
212 * Skip over an object in a stream. This method deserializes the object
213 * without producing a result or reading thru all bytes.
217 * @throws IOException
219 public abstract void skip(DataInput in, List<Object> identities) throws IOException;
220 public abstract void skip(DataInput in) throws IOException;
223 * Skip over an object in a stream. This method deserializes the object
224 * without producing a result or reading thru all bytes.
227 * @throws IOException
229 public void skip(InputStream in) throws IOException
231 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
236 * Get constant size of the data type in its binary serialized format
238 * @return size in bytes or null if not fixed
240 public abstract Integer getConstantSize();
243 * Get the number of bytes required to serialize an object
246 * @param identities thread local empty hash map
247 * @return number of bytes required to serialize obj
248 * @throws IOException
250 public abstract int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException;
251 public abstract int getSize(Object obj) throws IOException;
253 public abstract int getMinSize();
\r
256 * Serializes an object to a byte[].
259 * @return byte array containing the obj in its serialized format.
260 * @throws IOException
262 public byte[] serialize(Object obj) throws IOException
264 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
265 int size = getSize(obj, identities);
267 ByteBuffer buffer = ByteBuffer.allocate( size );
268 DataOutput writable = new ByteBufferWriteable( buffer );
269 serialize(writable, identities, obj);
271 return buffer.array();
275 * Serializes an object to an output stream.
276 * Note, if multiple objects are serialized to the same stream, it is
277 * more efficient to instantiate OutputStreamWriteable and identities only once.
281 * @throws IOException
283 public void serialize(Object obj, OutputStream out) throws IOException
285 // OutputStreamWriteable adapts OutputStream to DataOutput&BinaryWritable
286 OutputStreamWriteable writ = new OutputStreamWriteable(out);
287 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
288 serialize(writ, identities, obj);
292 * Serialize an object to a file. Note the type info is not written to the
\r
293 * file (unless obj is variant), and therefore is not compatible as .dbb
\r
296 * Databoard Binary file (.dbb) is a binary file that has datatype in the
\r
297 * header. To create .dbb file, serialize Datatype and then the value.
\r
298 * Or use methods in {@link Files} for convenience. Variant objects are, by
\r
299 * nature, .dbb compatible.
\r
303 * @throws IOException
\r
305 public void serialize(Object obj, File file)
\r
308 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
\r
309 BinaryFile writable = new BinaryFile( file );
\r
311 serialize(writable, identities, obj);
\r
318 * Get object as readable Input Stream.
321 * @return input stream
322 * @throws IOException
324 public InputStream getInputStream(Object obj) throws IOException
326 // Trivial implementation - better implementation would code bytes on-demend
\r
327 // without memory consumption.
328 byte[] data = serialize(obj);
329 return new ByteArrayInputStream(data);
333 * Serializer for data types that have referable objects
335 public static abstract class RecursiveSerializer extends Serializer {
\r
338 * Finalize the construction of the serializer. This is called once all component
\r
339 * serializers are constructed.
\r
341 public abstract void finalizeConstruction();
\r
343 public void serialize(DataOutput out, Object obj) throws IOException {
344 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
345 serialize(out, identities, obj);
347 public Object deserialize(DataInput in) throws IOException {
348 List<Object> identities = new ArrayList<Object>(0);
349 return deserialize(in, identities);
351 public void deserializeTo(DataInput in, Object obj) throws IOException {
\r
352 List<Object> identities = new ArrayList<Object>(0);
\r
353 deserializeTo(in, identities, obj);
\r
356 public void skip(DataInput in) throws IOException {
357 List<Object> identities = new ArrayList<Object>(0);
358 skip(in, identities);
361 public int getSize(Object obj)
363 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
364 return getSize(obj, identities);
369 * Serializer for non-recursive data types
371 public static abstract class NonRecursiveSerializer extends Serializer {
372 public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException {
375 public Object deserialize(DataInput in, List<Object> identities) throws IOException {
376 return deserialize(in);
378 public void deserializeTo(DataInput in, List<Object> identities, Object obj) throws IOException {
\r
379 deserializeTo(in, obj);
\r
382 public void skip(DataInput in, List<Object> identities) throws IOException {
386 public int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException {
392 * Serializer for composite data types
394 public static abstract class CompositeSerializer extends Serializer {
396 protected CompositeSerializer(boolean recursive) {
397 this.recursive = recursive;
401 * Finalize the construction of the serializer. This is called once all component
\r
402 * serializers are constructed.
\r
404 public abstract void finalizeConstruction( );
\r
406 public void serialize(DataOutput out, Object obj) throws IOException {
407 TObjectIntHashMap<Object> identities = recursive ? new TObjectIntHashMap<Object>(0) : null;
408 serialize(out, identities, obj);
410 public Object deserialize(DataInput in) throws IOException {
\r
411 List<Object> identities = recursive ? new ArrayList<Object>(0) : null;
\r
412 return deserialize(in, identities);
\r
414 public void deserializeTo(DataInput in, Object obj) throws IOException {
\r
415 List<Object> identities = recursive ? new ArrayList<Object>(0) : null;
\r
416 deserializeTo(in, identities, obj);
\r
419 public void skip(DataInput in) throws IOException {
420 List<Object> identities = recursive ? new ArrayList<Object>(0) : null;
421 skip(in, identities);
424 public int getSize(Object obj)
426 TObjectIntHashMap<Object> identities = recursive ? new TObjectIntHashMap<Object>(0) : null;
427 return getSize(obj, identities);
432 * Assert there are enough readable bytes. This method works only if input
\r
433 * object is instance of BinaryReadable. DataInput cannot tell the
\r
434 * number of remaining bytes.
\r
436 * This method is used by array, map, record and union sub-classes.
\r
439 * @throws IOException
\r
441 protected void assertRemainingBytes(DataInput in, long bts) throws IOException {
\r
442 if (in instanceof BinaryReadable == false) return;
\r
443 BinaryReadable r = (BinaryReadable) in;
\r
444 if (bts > r.length() - r.position()) throw new SerializationException("Malformed data. Serialization aborted. (Wrong binding?)");
\r