]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.databoard/src/org/simantics/databoard/serialization/Serializer.java
Re-implement URIStringUtils escape and unescape using Unicode
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / serialization / Serializer.java
1 /*******************************************************************************\r
2  *  Copyright (c) 2010 Association for Decentralized Information Management in\r
3  *  Industry THTH ry.\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
8  *\r
9  *  Contributors:\r
10  *      VTT Technical Research Centre of Finland - initial API and implementation\r
11  *******************************************************************************/\r
12 package org.simantics.databoard.serialization;
13
14 import gnu.trove.map.hash.TObjectIntHashMap;\r
15 \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
26 \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
34
35 /**\r
36  * Databoard binary serializer. \r
37  *\r
38  * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
39  */
40 public abstract class Serializer {
41
42         /**
43          * Serialize obj to out.
44          * 
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.  
49          * 
50          * @param out
51          * @param identities Thread local empty map or <code>null</code> if there is no recursion
52          * @param obj
53          * @throws IOException
54          */
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
58         {\r
59                 OutputStreamWriteable writ = new OutputStreamWriteable(out);\r
60                 serialize(writ, obj);           \r
61         }\r
62         
63         /**
64          * Deserialize an object from a readable.
65          * 
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
70          * \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
78          * readable.  
79          * 
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
83          * @throws IOException
84          */
85         public abstract Object deserialize(DataInput in, List<Object> identities) throws IOException;
86         public abstract Object deserialize(DataInput in) throws IOException;
87 \r
88         \r
89         /**\r
90          * Deserialize into an existing instance. This method writes over previous values.\r
91          * \r
92          * @param in\r
93          * @param identities\r
94          * @param dst valid object\r
95          * @throws IOException\r
96          */
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
99         \r
100         /**\r
101          * Attempt deserialize to existing instance. Creates new if not possible.  \r
102          * \r
103          * @param in\r
104          * @param identities\r
105          * @param dst\r
106          * @return dst or new obj\r
107          * @throws IOException\r
108          */\r
109         public Object deserializeToTry(DataInput in, List<Object> identities, Object dst) throws IOException \r
110         {\r
111                 deserializeTo(in, identities, dst);\r
112                 return dst;                             \r
113         }\r
114         
115         /**
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)}.
120          * 
121          * @param in
122          * @return The object deserialized into a Java Object
123          * @throws IOException
124          */
125         public Object deserialize(InputStream in) throws IOException
126         {
127                 // InputStreamReadable adapts InputStream to BinaryReadable&DataInput\r
128                 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
129                 return deserialize(read);
130         }\r
131         \r
132         /**\r
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
137          * \r
138          * @param in\r
139          * @param obj a valid object\r
140          * @throws IOException\r
141          */\r
142         public void deserialize(InputStream in, Object obj) throws IOException\r
143         {\r
144                 // InputStreamReadable adapts InputStream to BinaryReadable&DataInput\r
145                 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);\r
146                 deserializeTo(read, obj);\r
147         }       \r
148         \r
149         /**\r
150          * Deserialize object from a file.  \r
151          * \r
152          * @param file source file\r
153          * @return the object\r
154          * @throws IOException\r
155          */\r
156         public Object deserialize(File file) throws IOException\r
157         {\r
158                 BinaryFile f = new BinaryFile(file);\r
159                 try {\r
160                         return deserialize(f);\r
161                 } finally {\r
162                         f.close();\r
163                 }               \r
164         }\r
165         \r
166         /**\r
167          * Deserialize a file into a valid object. This method writes over previous values.\r
168          * \r
169          * @param file source file\r
170          * @param obj a dst valid object\r
171          * @throws IOException\r
172          */\r
173         public void deserialize(File file, Object obj) throws IOException\r
174         {\r
175                 BinaryFile f = new BinaryFile(file);\r
176                 try {\r
177                         deserializeTo(f, obj);\r
178                 } finally {\r
179                         f.close();\r
180                 }               \r
181         }       
182         
183         /**\r
184          * Deserialize an object in byte[] format.
185          * 
186          * @param data
187          * @return the instance
188          * @throws IOException
189          */
190         public Object deserialize(byte[] data) throws IOException
191         {
192                 ByteBuffer buffer = ByteBuffer.wrap( data );
193                 ByteBufferReadable readable = new ByteBufferReadable( buffer );
194                 return deserialize(readable);
195         }\r
196         \r
197         /**\r
198          * Deserialize byte[] into a valid object.\r
199          * \r
200          * @param data\r
201          * @param obj dst valid object\r
202          * @throws IOException\r
203          */\r
204         public void deserialize(byte[] data, Object obj) throws IOException\r
205         {\r
206                 ByteBuffer buffer = ByteBuffer.wrap( data );\r
207                 ByteBufferReadable readable = new ByteBufferReadable( buffer );\r
208                 deserializeTo(readable, obj);\r
209         }       
210
211         /**
212          * Skip over an object in a stream. This method deserializes the object 
213          * without producing a result or reading thru all bytes. 
214          * 
215          * @param in
216          * @param identities
217          * @throws IOException
218          */
219         public abstract void skip(DataInput in, List<Object> identities) throws IOException;
220         public abstract void skip(DataInput in) throws IOException;
221         
222         /**
223          * Skip over an object in a stream. This method deserializes the object 
224          * without producing a result or reading thru all bytes. 
225          * 
226          * @param in
227          * @throws IOException
228          */     
229         public void skip(InputStream in) throws IOException
230         {
231                 InputStreamReadable read = new InputStreamReadable(in, Long.MAX_VALUE);
232                 skip(read);
233         }
234         
235         /**
236          * Get constant size of the data type in its binary serialized format 
237          * 
238          * @return size in bytes or null if not fixed
239          */
240         public abstract Integer getConstantSize();
241         
242         /**\r
243          * Get the number of bytes required to serialize an object
244          * 
245          * @param obj
246          * @param identities thread local empty hash map
247          * @return number of bytes required to serialize obj
248          * @throws IOException 
249          */
250         public abstract int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException;
251         public abstract int getSize(Object obj) throws IOException;
252 \r
253         public abstract int getMinSize();\r
254         
255         /**
256          * Serializes an object to a byte[].
257          * 
258          * @param obj
259          * @return byte array containing the obj in its serialized format. 
260          * @throws IOException
261          */
262         public byte[] serialize(Object obj) throws IOException
263         {
264                 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();
265                 int size = getSize(obj, identities);
266                 identities.clear();
267                 ByteBuffer buffer = ByteBuffer.allocate( size );
268                 DataOutput writable = new ByteBufferWriteable( buffer );
269                 serialize(writable, identities, obj);
270                 buffer.rewind();
271                 return buffer.array();
272         }
273         
274         /**
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.
278          * 
279          * @param obj
280          * @param out
281          * @throws IOException
282          */
283         public void serialize(Object obj, OutputStream out) throws IOException
284         {\r
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);
289         }\r
290         \r
291         /**\r
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
294          * file. \r
295          * \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
300          * \r
301          * @param obj\r
302          * @param file\r
303          * @throws IOException\r
304          */\r
305         public void serialize(Object obj, File file)\r
306         throws IOException\r
307         {\r
308                 TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>();\r
309                 BinaryFile writable = new BinaryFile( file );\r
310                 try {\r
311                         serialize(writable, identities, obj);\r
312                 } finally {\r
313                         writable.close();\r
314                 }\r
315         }       
316
317         /**
318          * Get object as readable Input Stream.
319          * 
320          * @param obj
321          * @return input stream
322          * @throws IOException
323          */
324         public InputStream getInputStream(Object obj) throws IOException
325         {
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);
330         }
331         
332         /**
333          * Serializer for data types that have referable objects
334          */
335         public static abstract class RecursiveSerializer extends Serializer {\r
336                 \r
337                 /**\r
338                  * Finalize the construction of the serializer. This is called once all component\r
339                  * serializers are constructed.\r
340                  */\r
341                 public abstract void finalizeConstruction();\r
342                 
343                 public void serialize(DataOutput out, Object obj) throws IOException {
344                         TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
345                         serialize(out, identities, obj);
346                 }
347                 public Object deserialize(DataInput in) throws IOException {
348                         List<Object> identities = new ArrayList<Object>(0);
349                         return deserialize(in, identities);
350                 }
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
354                 }\r
355                 @Override
356                 public void skip(DataInput in) throws IOException {
357                         List<Object> identities = new ArrayList<Object>(0);
358                         skip(in, identities);
359                 }
360                 @Override
361                 public int getSize(Object obj)
362                                 throws IOException {
363                         TObjectIntHashMap<Object> identities = new TObjectIntHashMap<Object>(0);
364                         return getSize(obj, identities);
365                 }
366         }
367         
368         /**
369          * Serializer for non-recursive data types
370          */
371         public static abstract class NonRecursiveSerializer extends Serializer {
372                 public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException {
373                         serialize(out, obj);
374                 }
375                 public Object deserialize(DataInput in, List<Object> identities) throws IOException {
376                         return deserialize(in);
377                 }\r
378                 public void deserializeTo(DataInput in, List<Object> identities, Object obj) throws IOException {\r
379                         deserializeTo(in, obj);\r
380                 }               
381                 @Override
382                 public void skip(DataInput in, List<Object> identities) throws IOException {
383                         skip(in);
384                 }
385                 @Override
386                 public int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException {
387                         return getSize(obj);
388                 }
389         }
390         
391         /**
392          * Serializer for composite data types
393          */
394         public static abstract class CompositeSerializer extends Serializer {
395                 boolean recursive;
396                 protected CompositeSerializer(boolean recursive) {
397                         this.recursive = recursive;
398                 }\r
399                 \r
400                 /**\r
401                  * Finalize the construction of the serializer. This is called once all component\r
402                  * serializers are constructed.\r
403                  */\r
404                 public abstract void finalizeConstruction( );\r
405                 
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);
409                 }
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
413                 }\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
417                 }\r
418                 @Override
419                 public void skip(DataInput in) throws IOException {
420                         List<Object> identities = recursive ? new ArrayList<Object>(0) : null;
421                         skip(in, identities);
422                 }
423                 @Override
424                 public int getSize(Object obj)
425                                 throws IOException {
426                         TObjectIntHashMap<Object> identities = recursive ? new TObjectIntHashMap<Object>(0) : null;
427                         return getSize(obj, identities);
428                 }
429         }
430 \r
431         /**\r
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
435          * \r
436          * This method is used by array, map, record and union sub-classes.\r
437          * \r
438          * @param in\r
439          * @throws IOException\r
440          */\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
445         }\r
446         
447 }
448