]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/serialization/Serializer.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / serialization / Serializer.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/serialization/Serializer.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/serialization/Serializer.java
new file mode 100644 (file)
index 0000000..7082969
--- /dev/null
@@ -0,0 +1,448 @@
+/*******************************************************************************\r
+ *  Copyright (c) 2010 Association for Decentralized Information Management in\r
+ *  Industry THTH ry.\r
+ *  All rights reserved. This program and the accompanying materials\r
+ *  are made available under the terms of the Eclipse Public License v1.0\r
+ *  which accompanies this distribution, and is available at\r
+ *  http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ *  Contributors:\r
+ *      VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.serialization;
+
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.ByteArrayInputStream;\r
+import java.io.DataInput;\r
+import java.io.DataOutput;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.InputStream;\r
+import java.io.OutputStream;\r
+import java.nio.ByteBuffer;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+\r
+import org.simantics.databoard.Files;\r
+import org.simantics.databoard.util.binary.BinaryFile;\r
+import org.simantics.databoard.util.binary.BinaryReadable;\r
+import org.simantics.databoard.util.binary.ByteBufferReadable;\r
+import org.simantics.databoard.util.binary.ByteBufferWriteable;\r
+import org.simantics.databoard.util.binary.InputStreamReadable;\r
+import org.simantics.databoard.util.binary.OutputStreamWriteable;\r
+
+/**\r
+ * Databoard binary serializer. \r
+ *\r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+ */
+public abstract class Serializer {
+
+       /**
+        * Serialize obj to out.
+        * 
+        * The identities argument is a map of identities (in the binary block) and objects
+        * that have already been serialized. Once serialized, an object is added to the map.
+        * Typically an empty map is provided. If the type has no recursion, i.e. Referable \r
+        * Records, a <code>null</code> value can be provided.  
+        * 
+        * @param out
+        * @param identities Thread local empty map or <code>null</code> if there is no recursion
+        * @param obj
+        * @throws IOException
+        */
+       public abstract void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException;
+       public abstract void serialize(DataOutput out, Object obj) throws IOException;
+       public void serialize(OutputStream out, Object obj) throws IOException\r
+       {\r
+               OutputStreamWriteable writ = new OutputStreamWriteable(out);\r
+               serialize(writ, obj);           \r
+       }\r
+       
+       /**
+        * Deserialize an object from a readable.
+        * 
+        * The identities argument is a list of identities (in the binary block) of objects
+        * that have already been deserialized. Once deserialized they are added to the list.
+        * Typically an empty list is provided. If the type has no recursion, i.e. Referable\r
+        * Records, a <code>null</code> value can be provided.<p>\r
+        * \r
+        * Note, if in argument is instanceof BinaryReadable or RandomAccessBinary, \r
+        * the serializer performs extra protection against malformed data when \r
+        * deserializing arrays and maps. This prevents the serializer from \r
+        * instanting potentially out-of-memory-invoking huge arrays. For example, \r
+        * if data data says array size is 0xffffffff (-1), 4GB is allocated -> \r
+        * out of memory exception -> unhandled runtime error. BinaryReadable has \r
+        * length limit which allowes serializer to estimate whether future data is \r
+        * readable.  
+        * 
+        * @param in DataInput, BinaryReadable or RandomAccessBinary
+        * @param identities empty identities array or <code>null</code> if there is no recursion
+        * @return the instance
+        * @throws IOException
+        */
+       public abstract Object deserialize(DataInput in, List<Object> identities) throws IOException;
+       public abstract Object deserialize(DataInput in) throws IOException;
+\r
+       \r
+       /**\r
+        * Deserialize into an existing instance. This method writes over previous values.\r
+        * \r
+        * @param in\r
+        * @param identities\r
+        * @param dst valid object\r
+        * @throws IOException\r
+        */
+       public abstract void deserializeTo(DataInput in, List<Object> identities, Object dst) throws IOException;\r
+       public abstract void deserializeTo(DataInput in, Object dst) throws IOException;\r
+       \r
+       /**\r
+        * Attempt deserialize to existing instance. Creates new if not possible.  \r
+        * \r
+        * @param in\r
+        * @param identities\r
+        * @param dst\r
+        * @return dst or new obj\r
+        * @throws IOException\r
+        */\r
+       public Object deserializeToTry(DataInput in, List<Object> identities, Object dst) throws IOException \r
+       {\r
+               deserializeTo(in, identities, dst);\r
+               return dst;                             \r
+       }\r
+       
+       /**
+        * Deserialize the next object in an input stream.
+        * Note, if multiple objects are deserialized from the same stream, it is 
+        * more efficient to instantiate InputStreamReadable and identities only once,\r
+        * and use {@link #deserialize(DataInput, List)}.
+        * 
+        * @param in
+        * @return The object deserialized into a Java Object
+        * @throws IOException
+        */
+       public Object deserialize(InputStream in) throws IOException
+       {
+               // InputStreamReadable adapts InputStream to BinaryReadable&DataInput\r
+               InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
+               return deserialize(read);
+       }\r
+       \r
+       /**\r
+        * Deserialize from an input stream into an object.\r
+        * Note, if multiple objects are deserialized from the same stream, it is \r
+        * more efficient to instantiate InputStreamReadable and identities only once,\r
+        * and use {@link #deserialize(DataInput, List)}.\r
+        * \r
+        * @param in\r
+        * @param obj a valid object\r
+        * @throws IOException\r
+        */\r
+       public void deserialize(InputStream in, Object obj) throws IOException\r
+       {\r
+               // InputStreamReadable adapts InputStream to BinaryReadable&DataInput\r
+               InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);\r
+               deserializeTo(read, obj);\r
+       }       \r
+       \r
+       /**\r
+        * Deserialize object from a file.  \r
+        * \r
+        * @param file source file\r
+        * @return the object\r
+        * @throws IOException\r
+        */\r
+       public Object deserialize(File file) throws IOException\r
+       {\r
+               BinaryFile f = new BinaryFile(file);\r
+               try {\r
+                       return deserialize(f);\r
+               } finally {\r
+                       f.close();\r
+               }               \r
+       }\r
+       \r
+       /**\r
+        * Deserialize a file into a valid object. This method writes over previous values.\r
+        * \r
+        * @param file source file\r
+        * @param obj a dst valid object\r
+        * @throws IOException\r
+        */\r
+       public void deserialize(File file, Object obj) throws IOException\r
+       {\r
+               BinaryFile f = new BinaryFile(file);\r
+               try {\r
+                       deserializeTo(f, obj);\r
+               } finally {\r
+                       f.close();\r
+               }               \r
+       }       
+       
+       /**\r
+        * Deserialize an object in byte[] format.
+        * 
+        * @param data
+        * @return the instance
+        * @throws IOException
+        */
+       public Object deserialize(byte[] data) throws IOException
+       {
+               ByteBuffer buffer = ByteBuffer.wrap( data );
+               ByteBufferReadable readable = new ByteBufferReadable( buffer );
+               return deserialize(readable);
+       }\r
+       \r
+       /**\r
+        * Deserialize byte[] into a valid object.\r
+        * \r
+        * @param data\r
+        * @param obj dst valid object\r
+        * @throws IOException\r
+        */\r
+       public void deserialize(byte[] data, Object obj) throws IOException\r
+       {\r
+               ByteBuffer buffer = ByteBuffer.wrap( data );\r
+               ByteBufferReadable readable = new ByteBufferReadable( buffer );\r
+               deserializeTo(readable, obj);\r
+       }       
+
+       /**
+        * Skip over an object in a stream. This method deserializes the object 
+        * without producing a result or reading thru all bytes. 
+        * 
+        * @param in
+        * @param identities
+        * @throws IOException
+        */
+       public abstract void skip(DataInput in, List<Object> identities) throws IOException;
+       public abstract void skip(DataInput in) throws IOException;
+       
+       /**
+        * Skip over an object in a stream. This method deserializes the object 
+        * without producing a result or reading thru all bytes. 
+        * 
+        * @param in
+        * @throws IOException
+        */     
+       public void skip(InputStream in) throws IOException
+       {
+               InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
+               skip(read);
+       }
+       
+       /**
+        * Get constant size of the data type in its binary serialized format 
+        * 
+        * @return size in bytes or null if not fixed
+        */
+       public abstract Integer getConstantSize();
+       
+       /**\r
+        * Get the number of bytes required to serialize an object
+        * 
+        * @param obj
+        * @param identities thread local empty hash map
+        * @return number of bytes required to serialize obj
+        * @throws IOException 
+        */
+       public abstract int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException;
+       public abstract int getSize(Object obj) throws IOException;
+\r
+       public abstract int getMinSize();\r
+       
+       /**
+        * Serializes an object to a byte[].
+        * 
+        * @param obj
+        * @return byte array containing the obj in its serialized format. 
+        * @throws IOException
+        */
+       public byte[] serialize(Object obj) throws IOException
+       {
+               TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
+               int size = getSize(obj, identities);
+               identities.clear();
+               ByteBuffer buffer = ByteBuffer.allocate( size );
+               DataOutput writable = new ByteBufferWriteable( buffer );
+               serialize(writable, identities, obj);
+               buffer.rewind();
+               return buffer.array();
+       }
+       
+       /**
+        * Serializes an object to an output stream.
+        * Note, if multiple objects are serialized to the same stream, it is 
+        * more efficient to instantiate OutputStreamWriteable and identities only once.
+        * 
+        * @param obj
+        * @param out
+        * @throws IOException
+        */
+       public void serialize(Object obj, OutputStream out) throws IOException
+       {\r
+               // OutputStreamWriteable adapts OutputStream to DataOutput&BinaryWritable
+               OutputStreamWriteable writ = new OutputStreamWriteable(out);
+               TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
+               serialize(writ, identities, obj);
+       }\r
+       \r
+       /**\r
+        * Serialize an object to a file. Note the type info is not written to the\r
+        * file (unless obj is variant), and therefore is not compatible as .dbb \r
+        * file. \r
+        * \r
+        * Databoard Binary file (.dbb) is a binary file that has datatype in the \r
+        * header. To create .dbb file, serialize Datatype and then the value.\r
+        * Or use methods in {@link Files} for convenience. Variant objects are, by\r
+        * nature, .dbb compatible.   \r
+        * \r
+        * @param obj\r
+        * @param file\r
+        * @throws IOException\r
+        */\r
+       public void serialize(Object obj, File file)\r
+       throws IOException\r
+       {\r
+               TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();\r
+               BinaryFile writable = new BinaryFile( file );\r
+               try {\r
+                       serialize(writable, identities, obj);\r
+               } finally {\r
+                       writable.close();\r
+               }\r
+       }       
+
+       /**
+        * Get object as readable Input Stream.
+        * 
+        * @param obj
+        * @return input stream
+        * @throws IOException
+        */
+       public InputStream getInputStream(Object obj) throws IOException
+       {
+               // Trivial implementation - better implementation would code bytes on-demend\r
+               // without memory consumption.
+               byte[] data = serialize(obj);
+               return new ByteArrayInputStream(data);
+       }
+       
+       /**
+        * Serializer for data types that have referable objects
+        */
+       public static abstract class RecursiveSerializer extends Serializer {\r
+               \r
+               /**\r
+                * Finalize the construction of the serializer. This is called once all component\r
+                * serializers are constructed.\r
+                */\r
+               public abstract void finalizeConstruction();\r
+               
+               public void serialize(DataOutput out, Object obj) throws IOException {
+                       TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
+                       serialize(out, identities, obj);
+               }
+               public Object deserialize(DataInput in) throws IOException {
+                       List<Object> identities = new ArrayList<Object>(0);
+                       return deserialize(in, identities);
+               }
+               public void deserializeTo(DataInput in, Object obj) throws IOException {\r
+                       List<Object> identities = new ArrayList<Object>(0);\r
+                       deserializeTo(in, identities, obj);\r
+               }\r
+               @Override
+               public void skip(DataInput in) throws IOException {
+                       List<Object> identities = new ArrayList<Object>(0);
+                       skip(in, identities);
+               }
+               @Override
+               public int getSize(Object obj)
+                               throws IOException {
+                       TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
+                       return getSize(obj, identities);
+               }
+       }
+       
+       /**
+        * Serializer for non-recursive data types
+        */
+       public static abstract class NonRecursiveSerializer extends Serializer {
+               public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException {
+                       serialize(out, obj);
+               }
+               public Object deserialize(DataInput in, List<Object> identities) throws IOException {
+                       return deserialize(in);
+               }\r
+               public void deserializeTo(DataInput in, List<Object> identities, Object obj) throws IOException {\r
+                       deserializeTo(in, obj);\r
+               }               
+               @Override
+               public void skip(DataInput in, List<Object> identities) throws IOException {
+                       skip(in);
+               }
+               @Override
+               public int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException {
+                       return getSize(obj);
+               }
+       }
+       
+       /**
+        * Serializer for composite data types
+        */
+       public static abstract class CompositeSerializer extends Serializer {
+               boolean recursive;
+               protected CompositeSerializer(boolean recursive) {
+                       this.recursive = recursive;
+               }\r
+               \r
+               /**\r
+                * Finalize the construction of the serializer. This is called once all component\r
+                * serializers are constructed.\r
+                */\r
+               public abstract void finalizeConstruction( );\r
+               
+               public void serialize(DataOutput out, Object obj) throws IOException {
+                       TObjectIntHashMap<Object> identities = recursive ? new TObjectIntHashMap<Object>(0) : null;
+                       serialize(out, identities, obj);
+               }
+               public Object deserialize(DataInput in) throws IOException {\r
+                       List<Object> identities = recursive ? new ArrayList<Object>(0) : null;\r
+                       return deserialize(in, identities);\r
+               }\r
+               public void deserializeTo(DataInput in, Object obj) throws IOException {\r
+                       List<Object> identities = recursive ? new ArrayList<Object>(0) : null;\r
+                       deserializeTo(in, identities, obj);\r
+               }\r
+               @Override
+               public void skip(DataInput in) throws IOException {
+                       List<Object> identities = recursive ? new ArrayList<Object>(0) : null;
+                       skip(in, identities);
+               }
+               @Override
+               public int getSize(Object obj)
+                               throws IOException {
+                       TObjectIntHashMap<Object> identities = recursive ? new TObjectIntHashMap<Object>(0) : null;
+                       return getSize(obj, identities);
+               }
+       }
+\r
+       /**\r
+        * Assert there are enough readable bytes. This method works only if input \r
+        * object is instance of BinaryReadable. DataInput cannot tell the \r
+        * number of remaining bytes.   \r
+        * \r
+        * This method is used by array, map, record and union sub-classes.\r
+        * \r
+        * @param in\r
+        * @throws IOException\r
+        */\r
+       protected void assertRemainingBytes(DataInput in, long bts) throws IOException {\r
+               if (in instanceof BinaryReadable == false) return;\r
+               BinaryReadable r = (BinaryReadable) in;\r
+               if (bts > r.length() - r.position()) throw new SerializationException("Malformed data. Serialization aborted. (Wrong binding?)");\r
+       }\r
+       
+}
+