package org.simantics.databoard.serialization.impl; import gnu.trove.map.hash.TObjectIntHashMap; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.List; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.util.IsReferableQuery; import org.simantics.databoard.binding.util.Result; import org.simantics.databoard.serialization.SerializationException; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.Serializer.CompositeSerializer; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.binary.Endian; public class UnionSerializer extends CompositeSerializer { UnionBinding binding; public Serializer[] componentSerializers; int tagTypeCount; Integer fixedSize; int minSize; /** * * @param binding * @param componentSerializers (optional) can be set later */ public UnionSerializer(UnionBinding binding, Serializer[] componentSerializers) { super( IsReferableQuery.isReferable( binding.type() ) != Result.No ); tagTypeCount = ((UnionType) binding.type()).components.length; this.binding = binding; this.componentSerializers = componentSerializers; } @Override public void finalizeConstruction() { // Calculate Fixed Size for (Serializer s : componentSerializers) { Integer fixedSizeOfComponentSerializer = s.getConstantSize(); if (fixedSizeOfComponentSerializer==null) { fixedSize = null; break; } if (fixedSize!=null && !fixedSize.equals(fixedSizeOfComponentSerializer)) { fixedSize = null; break; } fixedSize = fixedSizeOfComponentSerializer; } if (componentSerializers.length == 0) fixedSize = 0; if (fixedSize!=null) fixedSize += Endian.getUIntLength(tagTypeCount-1); if (componentSerializers.length>0) { minSize = Integer.MAX_VALUE; for (Serializer s : componentSerializers) minSize = Math.min(minSize, s.getMinSize()); minSize += Endian.getUIntLength( tagTypeCount-1 ); } } @Override public Object deserialize(DataInput in, List identities) throws IOException { try { int tag = Endian.getUInt(in, tagTypeCount-1); return binding.create(tag, componentSerializers[tag].deserialize(in, identities)); } catch (BindingException e) { throw new IOException( e ); } } public Object deserializeToTry(DataInput in, List identities, Object dst) throws IOException { if (binding.isTagMutable()) { return deserialize(in, identities); } else { deserializeTo(in, identities, dst); return dst; } } @Override public void deserializeTo(DataInput in, List identities, Object obj) throws IOException { try { int tag = Endian.getUInt(in, tagTypeCount-1); int oldTag = binding.getTag(obj); Serializer cs = componentSerializers[tag]; boolean csImmutable = binding.getComponentBinding(tag).isImmutable(); if (oldTag==tag && !csImmutable) { Object component = binding.getValue(obj); component = componentSerializers[tag].deserializeToTry(in, identities, component); binding.setValue(obj, tag, component); } else { Object component = cs.deserialize(in, identities); binding.setValue(obj, tag, component); } } catch (BindingException e) { throw new IOException( e ); } } @Override public void skip(DataInput in, List identities) throws IOException, SerializationException { int tag = Endian.getUInt(in, tagTypeCount-1); componentSerializers[tag].skip(in, identities); } @Override public void serialize(DataOutput out, TObjectIntHashMap identities, Object obj) throws IOException { try { int tag = binding.getTag(obj); Endian.putUInt(out, tag, tagTypeCount-1); componentSerializers[tag].serialize(out, identities, binding.getValue(obj)); } catch (BindingException e) { throw new IOException( e ); } } @Override public Integer getConstantSize() { return fixedSize; } @Override public int getSize(Object obj, TObjectIntHashMap identities) throws IOException { try { if (fixedSize!=null) return fixedSize; int tag = binding.getTag(obj); return Endian.getUIntLength( tagTypeCount-1 ) + componentSerializers[tag].getSize(binding.getValue(obj), identities); } catch (BindingException e) { throw new IOException( e ); } } @Override public int getMinSize() { return minSize; } }