--- /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.List;\r
+import java.util.TreeSet;\r
+\r
+import org.simantics.databoard.binding.MapBinding;\r
+import org.simantics.databoard.binding.error.BindingException;\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
+\r
+public class MapSerializer extends CompositeSerializer {\r
+\r
+ Integer fixedSizeOfKey, fixedSizeOfValue, fixedSizeOfEntry;\r
+ public Serializer keySerializer, valueSerializer;\r
+ MapBinding binding;\r
+ \r
+ /**\r
+ * \r
+ * @param binding\r
+ * @param keySerializer (optional) can be set later\r
+ * @param valueSerializer (optional) can be set later\r
+ */\r
+ public MapSerializer(MapBinding binding, Serializer keySerializer, Serializer valueSerializer) {\r
+ super( IsReferableQuery.isReferable( binding.type() ) != Result.No );\r
+ this.keySerializer = keySerializer;\r
+ this.valueSerializer = valueSerializer;\r
+ this.binding = binding;\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void finalizeConstruction() {\r
+ fixedSizeOfKey = keySerializer.getConstantSize();\r
+ fixedSizeOfValue = valueSerializer.getConstantSize();\r
+ fixedSizeOfEntry = (fixedSizeOfKey!=null && fixedSizeOfValue!=null) ? (fixedSizeOfKey+fixedSizeOfValue) : null; \r
+ }\r
+\r
+ @Override\r
+ public Object deserialize(DataInput in, List<Object> identities) throws IOException{\r
+ try {\r
+ int length = in.readInt();\r
+ if (length<0) throw new SerializationException("Cannot use negative array length");\r
+ assertRemainingBytes(in, (long)length*((long)keySerializer.getMinSize()+(long)valueSerializer.getMinSize()));\r
+ \r
+ Object keys[] = new Object[length];\r
+ Object values[] = new Object[length];\r
+ \r
+ for(int i=0;i<length;++i) {\r
+ keys[i] = keySerializer.deserialize(in, identities);\r
+ values[i] = valueSerializer.deserialize(in, identities); \r
+ }\r
+ return binding.create(keys, values);\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ } \r
+ }\r
+ \r
+ @Override\r
+ public void deserializeTo(DataInput in, List<Object> identities, Object obj) throws IOException {\r
+ try {\r
+ TreeSet<Object> oldKeys = new TreeSet<Object>( binding.getKeyBinding() );\r
+ binding.getKeys(obj, oldKeys);\r
+ \r
+ int length = in.readInt();\r
+ if (length<0) throw new SerializationException("Cannot use negative array length");\r
+ assertRemainingBytes(in, (long)length*((long)keySerializer.getMinSize()+(long)valueSerializer.getMinSize()));\r
+ \r
+ for(int i=0;i<length;++i) {\r
+ Object key = keySerializer.deserialize(in, identities);\r
+ Object value = valueSerializer.deserialize(in, identities);\r
+ binding.put(obj, key, value);\r
+ oldKeys.remove(key);\r
+ }\r
+ \r
+ // remove unused keys\r
+ for (Object key : oldKeys)\r
+ binding.remove(obj, key);\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ \r
+ }\r
+ \r
+ @Override\r
+ public void skip(DataInput in, List<Object> identities)\r
+ throws IOException, SerializationException {\r
+ int length = in.readInt();\r
+ if (fixedSizeOfEntry!=null) {\r
+ in.skipBytes( length * fixedSizeOfEntry );\r
+ return;\r
+ }\r
+ for(int i=0;i<length;++i) {\r
+ if (fixedSizeOfKey!=null) {\r
+ in.skipBytes(fixedSizeOfKey);\r
+ } else {\r
+ keySerializer.skip(in, identities);\r
+ }\r
+ \r
+ if (fixedSizeOfValue!=null) {\r
+ in.skipBytes(fixedSizeOfValue);\r
+ } else {\r
+ valueSerializer.skip(in, identities);\r
+ }\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public void serialize(DataOutput out, TObjectIntHashMap<Object> identities, Object map) throws IOException {\r
+ try {\r
+ int length = binding.size(map);\r
+ //if (binding.isOrderedMap(map)) \r
+ {\r
+ Object[] keys = new Object[length];\r
+ Object[] values = new Object[length];\r
+ binding.getAll(map, keys, values);\r
+ out.writeInt(length);\r
+ for(int i=0;i<length;++i) {\r
+ Object key = keys[i];\r
+ Object value = values[i];\r
+ keySerializer.serialize(out, identities, key);\r
+ valueSerializer.serialize(out, identities, value); \r
+ }\r
+ }\r
+ /*\r
+ else {\r
+ // Sort keys by putting into a treemap\r
+ TreeMap<Object, Object> copyMap = new TreeMap<Object, Object>(binding.getKeyBinding());\r
+ binding.getAll(map, copyMap);\r
+ Iterator<Entry<Object, Object>> iter = copyMap.entrySet().iterator();\r
+ putLength(out, length);\r
+ while (iter.hasNext()) {\r
+ Entry<Object, Object> e = iter.next();\r
+ Object key = e.getKey();\r
+ Object value = e.getValue();\r
+ keySerializer.serialize(out, identities, key);\r
+ valueSerializer.serialize(out, identities, value); \r
+ }\r
+ }*/\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ }\r
+\r
+ @Override\r
+ public Integer getConstantSize() {\r
+ return null;\r
+ }\r
+\r
+ @Override\r
+ public int getSize(Object obj, TObjectIntHashMap<Object> identities) throws IOException {\r
+ try {\r
+ int length = binding.size(obj);\r
+ if (fixedSizeOfEntry!=null)\r
+ return 4 + fixedSizeOfEntry * length;\r
+ int result = 4;\r
+ Object keys[] = binding.getKeys(obj);\r
+ for(int i=0;i<length;++i) {\r
+ Object key = keys[i];\r
+ Object value = binding.get(obj, key);\r
+ result += keySerializer.getSize(key, identities);\r
+ result += valueSerializer.getSize(value, identities);\r
+ }\r
+ return result;\r
+ } catch (BindingException e) {\r
+ throw new IOException( e ); \r
+ }\r
+ }\r
+\r
+ @Override\r
+ public int getMinSize() {\r
+ return 4;\r
+ }\r
+ \r
+} \r