--- /dev/null
+package org.simantics.databoard.serialization.impl;\r
+\r
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.DataInput;\r
+import java.io.DataOutput;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Iterator;\r
+import java.util.List;\r
+\r
+import org.simantics.databoard.binding.ArrayBinding;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.impl.ArrayListBinding;\r
+import org.simantics.databoard.binding.util.IsReferableQuery;\r
+import org.simantics.databoard.binding.util.Result;\r
+import org.simantics.databoard.serialization.SerializationException;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.serialization.Serializer.CompositeSerializer;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.util.Range;\r
+\r
+public class ArraySerializer extends CompositeSerializer {\r
+\r
+ Range length;\r
+ Integer fixedLength;\r
+ Integer fixedSize;\r
+ Integer fixedSizeOfComponent;\r
+ public Serializer componentSerializer;\r
+ boolean componentImmutable;\r
+ ArrayBinding binding;\r
+ DefaultValueIterator componentValueItr;\r
+ \r
+ /**\r
+ * \r
+ * @param binding\r
+ * @param componentSerializer (optional) can be set after\r
+ */\r
+ public ArraySerializer(ArrayBinding binding, Serializer componentSerializer) {\r
+ super( IsReferableQuery.isReferable( binding.type() ) != Result.No );\r
+ ArrayType arrayType = (ArrayType) binding.type();\r
+ this.componentSerializer = componentSerializer;\r
+ this.binding = binding;\r
+ this.length = arrayType.getLength();\r
+ this.componentImmutable = binding.getComponentBinding().isImmutable();\r
+ this.componentValueItr = new DefaultValueIterator();\r
+ componentValueItr.binding = binding.componentBinding;\r
+ }\r
+ \r
+ @Override\r
+ public void finalizeConstruction() {\r
+ fixedSizeOfComponent = componentSerializer.getConstantSize();\r
+ if (fixedSizeOfComponent!=null && length!=null && length.getLower().equals(length.getUpper()) && length.getLower().getValue()!=null)\r
+ {\r
+ fixedLength = length.getLower().getValue().intValue();\r
+ fixedSize = fixedLength * fixedSizeOfComponent;\r
+ } \r
+ }\r
+\r
+ @Override\r
+ public Object deserialize(DataInput in, List<Object> identities) throws IOException {\r
+ try {\r
+ int length = fixedLength != null ? fixedLength : in.readInt(); \r
+ if (length<0) throw new SerializationException("Cannot use negative array length");\r
+ assertRemainingBytes(in, ((long)length)*componentSerializer.getMinSize()); \r
+\r
+ ArrayList<Object> temp = new ArrayList<Object>(length);\r
+ for(int i=0;i<length;++i)\r
+ temp.add(componentSerializer.deserialize(in, identities));\r
+ return binding instanceof ArrayListBinding ? temp : binding.create(temp);\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ }\r
+ \r
+ public Object deserializeToTry(DataInput in, List<Object> identities, Object obj) throws IOException\r
+ {\r
+ try {\r
+ int length = fixedLength != null ? fixedLength : in.readInt();\r
+ if (length<0) throw new SerializationException("Cannot use negative array length");\r
+ assertRemainingBytes(in, ((long)length)*componentSerializer.getMinSize()); \r
+ \r
+ int oldLen = binding.size(obj);\r
+ \r
+ if (oldLen!=length) {\r
+ if ( binding.isResizable() ) {\r
+ binding.setSize(obj, length);\r
+ } else {\r
+ obj = binding.create(length, componentValueItr);\r
+ oldLen = length;\r
+ }\r
+ }\r
+ \r
+ if ( componentImmutable ) {\r
+ for(int i=0;i<length;i++) {\r
+ Object component = componentSerializer.deserialize(in, identities);\r
+ binding.set(obj, i, component);\r
+ }\r
+ } else {\r
+ for(int i=0;i<length;i++) {\r
+ Object component = binding.get(obj, i);\r
+ component = componentSerializer.deserializeToTry(in, identities, component);\r
+ binding.set(obj, i, component); \r
+ }\r
+ if (oldLen>length) binding.setSize(obj, length); \r
+ }\r
+ \r
+ return obj;\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ \r
+ } \r
+\r
+ @Override\r
+ public void deserializeTo(DataInput in, List<Object> identities, Object obj) throws IOException {\r
+ try {\r
+ int length = fixedLength != null ? fixedLength : in.readInt();\r
+ if (length<0) throw new SerializationException("Cannot use negative array length");\r
+ assertRemainingBytes(in, ((long)length)*componentSerializer.getMinSize()); \r
+ \r
+ int oldLen = binding.size(obj);\r
+ \r
+ if (oldLen!=length) {\r
+ if ( binding.isResizable() ) {\r
+ binding.setSize(obj, length);\r
+ } else {\r
+ throw new IOException("Cannot resize array");\r
+ }\r
+ }\r
+ \r
+ if ( componentImmutable ) {\r
+ for(int i=0;i<length;i++) {\r
+ Object component = componentSerializer.deserialize(in, identities);\r
+ binding.set(obj, i, component);\r
+ }\r
+ } else {\r
+ for(int i=0;i<length;i++) {\r
+ Object component = binding.get(obj, i);\r
+ component = componentSerializer.deserializeToTry(in, identities, component);\r
+ binding.set(obj, i, component); \r
+ }\r
+ if (oldLen>length) binding.setSize(obj, length); \r
+ }\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ } \r
+ }\r
+ \r
+ @Override\r
+ public void skip(DataInput in, List<Object> identities)\r
+ throws IOException {\r
+ if (fixedSize!=null) {\r
+ in.skipBytes(fixedSize);\r
+ return;\r
+ }\r
+ int length = fixedLength != null ? fixedLength : in.readInt();\r
+ \r
+ if (fixedSizeOfComponent!=null) {\r
+ // Skip all elements\r
+ in.skipBytes(length * fixedSizeOfComponent);\r
+ } else {\r
+ // Skip each element individualy\r
+ for(int i=0;i<length;++i)\r
+ componentSerializer.skip(in, identities);\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object obj) throws IOException {\r
+ try {\r
+ int length = binding.size(obj);\r
+ if (fixedLength==null) {\r
+ out.writeInt(length);\r
+ } else { \r
+ if (length!=fixedLength)\r
+ throw new SerializationException("Unexpected array length "+length+", size is restricted to "+fixedLength);\r
+ }\r
+ \r
+ // Serialize elements\r
+ for(int i=0;i<length;++i)\r
+ componentSerializer.serialize(out, identities, binding.get(obj, i));\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Integer getConstantSize() {\r
+ return fixedSize;\r
+ }\r
+\r
+ @Override\r
+ public int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException {\r
+ try {\r
+ if (fixedSize!=null) return fixedSize;\r
+ int length = binding.size(obj);\r
+ if (fixedSizeOfComponent!=null)\r
+ return 4 + fixedSizeOfComponent * length;\r
+ int result = 4;\r
+ for(int i=0;i<length;++i)\r
+ result += componentSerializer.getSize(binding.get(obj, i), identities);\r
+ return result;\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getMinSize() {\r
+ return fixedSize != null ? fixedSize : 4;\r
+ }\r
+ \r
+}\r
+\r
+/**\r
+ * Default value iterator iterates default values.\r
+ * The iterator is infinite.\r
+ * \r
+ * @author toni.kalajainen\r
+ */\r
+class DefaultValueIterator implements Iterator<Object> {\r
+ \r
+ Binding binding;\r
+\r
+ @Override\r
+ public boolean hasNext() {\r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public Object next() {\r
+ try {\r
+ return binding.createDefault();\r
+ } catch (BindingException e) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void remove() {\r
+ }\r
+ \r
+}
\ No newline at end of file