]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryUnion.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / accessor / binary / BinaryUnion.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryUnion.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/accessor/binary/BinaryUnion.java
new file mode 100644 (file)
index 0000000..915722c
--- /dev/null
@@ -0,0 +1,408 @@
+/*******************************************************************************\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.accessor.binary;
+
+import gnu.trove.map.hash.TObjectIntHashMap;\r
+\r
+import java.io.IOException;\r
+import java.lang.ref.SoftReference;\r
+import java.util.ArrayList;\r
+import java.util.List;\r
+import java.util.concurrent.Executor;\r
+\r
+import org.simantics.databoard.accessor.Accessor;\r
+import org.simantics.databoard.accessor.UnionAccessor;\r
+import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
+import org.simantics.databoard.accessor.error.AccessorException;\r
+import org.simantics.databoard.accessor.error.ReferenceException;\r
+import org.simantics.databoard.accessor.event.Event;\r
+import org.simantics.databoard.accessor.event.UnionValueAssigned;\r
+import org.simantics.databoard.accessor.event.ValueAssigned;\r
+import org.simantics.databoard.accessor.file.FileUnionAccessor;\r
+import org.simantics.databoard.accessor.impl.AccessorParams;\r
+import org.simantics.databoard.accessor.impl.ListenerEntry;\r
+import org.simantics.databoard.accessor.interestset.InterestSet;\r
+import org.simantics.databoard.accessor.interestset.UnionInterestSet;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.ComponentReference;\r
+import org.simantics.databoard.accessor.reference.IndexReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.accessor.reference.NameReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.Binding;\r
+import org.simantics.databoard.binding.UnionBinding;\r
+import org.simantics.databoard.binding.error.BindingConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.mutable.MutableVariant;\r
+import org.simantics.databoard.serialization.Serializer;\r
+import org.simantics.databoard.serialization.SerializerConstructionException;\r
+import org.simantics.databoard.type.Datatype;\r
+import org.simantics.databoard.type.UnionType;\r
+import org.simantics.databoard.util.binary.Blob;\r
+import org.simantics.databoard.util.binary.Endian;\r
+
+public class BinaryUnion extends BinaryObject implements UnionAccessor, FileUnionAccessor {
+
+       /** Accessor to childr */
+       SoftReference<BinaryObject> component;  
+       
+       public BinaryUnion(BinaryObject parent, Blob blob, Datatype type, AccessorParams params) {
+               super(parent, blob, type, params);
+       }
+       
+       public UnionType type() {
+               return (UnionType) type;
+       }
+
+       @Override
+       public int count() {
+               return type().getComponentCount();
+       }
+
+       @Override
+       public void setValueNoflush(Binding binding, Object newValue)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       UnionBinding ub = (UnionBinding) binding;
+                       int tag = ub.getTag(newValue);
+                       Binding cb = ub.getComponentBinding(tag);
+                       Object cv = ub.getValue(newValue);
+                       setComponentValueNoflush(tag, cb, cv);
+               } catch (BindingException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+       
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T extends Accessor> T getComponentAccessor()
+                       throws AccessorConstructionException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       // Get existing or create new
+                       BinaryObject sa = getExistingAccessor();
+
+                       if (sa==null) {
+                               // Read Value                           
+                               b.position(0L);
+                               int tag = Endian.getUInt(b, count()-1);
+                               int tagLen = (int) b.position();
+                               Datatype ct = type().getComponent(tag).type;
+                               
+                               // Instantiate sub accessor. 
+                               sa = createSubAccessor(ct, tagLen, b.length()-tagLen, params);
+                               component = new SoftReference<BinaryObject>(sa);
+                               
+                               // Add listener to component, if it is in our interest set
+                               ListenerEntry le = listeners;
+                               while (le!=null) {                              
+                                       UnionInterestSet is = le.getInterestSet();
+                                       InterestSet cis = is.getComponentInterest(tag);
+                                       if (cis != null) {
+                                               try {
+                                                       ChildReference childPath = ChildReference.concatenate(le.path, new ComponentReference() );
+                                                       sa.addListener(le.listener, cis, childPath, le.executor);
+                                               } catch (AccessorException e) {
+                                                       throw new AccessorConstructionException(e);
+                                               }
+                                       }
+                                       le = le.next;
+                               }                               
+
+                       }
+                       
+                       return (T) sa;
+               } catch (IOException e) {
+                       throw new AccessorConstructionException( e );
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+       
+       /**
+        * Get existing sub-accessor
+        * 
+        * @return sub-accessor or <code>null</code>
+        */
+       BinaryObject getExistingAccessor() {
+               SoftReference<BinaryObject> c = component;
+               if (c==null) return null;
+               return c.get();         
+       }       
+
+       @Override
+       public Object getComponentValue(Binding componentBinding)
+                       throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       b.position( 0L );
+                       int tag = Endian.getUInt(b, count()-1);
+                       Datatype ct = type().getComponent(tag).type; 
+                       if ( !ct.equals(componentBinding.type()) ) {
+                               throw new AccessorException("Binding of "+ct+" expected.");
+                       }
+                       
+                       List<Object> ids = new ArrayList<Object>(0);
+                       Serializer s = params.serializerScheme.getSerializer( componentBinding );
+                       return s.deserialize(b, ids);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public int getTag() throws AccessorException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       b.position(0L);
+                       return Endian.getUInt(b, count()-1);
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public void setComponentValue(int tag, Binding componentBinding,
+                       Object componentValue) throws AccessorException {\r
+               assert b.isOpen();\r
+               writeLock();\r
+               try {
+                       setComponentValueNoflush(tag, componentBinding, componentValue);
+                       flush();\r
+               } finally {\r
+                       writeUnlock();\r
+               }
+       }
+
+       @SuppressWarnings("unchecked")
+       @Override
+       public <T extends Accessor> T getComponent(ChildReference reference)
+                       throws AccessorConstructionException {\r
+               assert b.isOpen();\r
+               readLock();
+               try {
+                       if (reference==null) return (T) this;\r
+                       \r
+                       if (reference instanceof LabelReference) {\r
+                               LabelReference lr = (LabelReference) reference;\r
+                               Integer tag = type().getComponentIndex( lr.label );\r
+                               \r
+                               if (tag==null && lr.label.equals("uv")) {\r
+                                       Accessor result = getComponentAccessor();\r
+                                       if (reference.getChildReference() != null)\r
+                                               result = result.getComponent(reference.getChildReference());\r
+                                       return (T) result;\r
+                               } else if (tag==null) {\r
+                                       throw new ReferenceException("Tag \""+lr.label+"\" not found");\r
+                               }\r
+                               \r
+                               if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");\r
+                               Accessor result = getComponentAccessor();\r
+                               if (reference.getChildReference() != null)\r
+                                       result = result.getComponent(reference.getChildReference());\r
+                               return (T) result;                              \r
+                       }\r
+                       
+                       if (reference instanceof ComponentReference) {
+                               Accessor result = getComponentAccessor();
+                               if (reference.getChildReference() != null)
+                                       result = result.getComponent(reference.getChildReference());
+                               return (T) result;
+                       } \r
+                       \r
+                       if (reference instanceof IndexReference) {
+                               IndexReference ir = (IndexReference) reference;
+                               if (ir.index<0 || ir.index>=type().getComponentCount()) throw new ReferenceException("Tag index out of bounds");
+                               if (ir.index != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(ir.index).name+")");
+                               Accessor result = getComponentAccessor();
+                               if (reference.getChildReference() != null)
+                                       result = result.getComponent(reference.getChildReference());
+                               return (T) result;
+                       } \r
+                       \r
+                       if (reference instanceof NameReference) {
+                               NameReference nr = (NameReference) reference;
+                               Integer tag = type().getComponentIndex( nr.name );
+                               if (tag==null) throw new ReferenceException("Tag \""+nr.name+"\" not found");
+                               if (tag != getTag()) throw new ReferenceException("The union isn't currently assigned with the expected type ("+type().getComponent(tag).name+")");
+                               Accessor result = getComponentAccessor();
+                               if (reference.getChildReference() != null)
+                                       result = result.getComponent(reference.getChildReference());
+                               return (T) result;                              
+                       }\r
+                       
+                       throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType");
+               } catch (AccessorException ae) {
+                       throw new AccessorConstructionException(ae);
+               } finally {\r
+                       readUnlock();\r
+               }
+       }
+
+       @Override
+       public void setComponentValueNoflush(int tag, Binding cb,
+                       Object cv) throws AccessorException {
+               assert b.isOpen();\r
+               writeLock();
+               try {
+                       int oldTag = getTag();
+                       int newTag = tag;                       
+                       boolean hadSameTag = oldTag == newTag;
+                       
+                       // Write to component
+                       BinaryObject sa = getExistingAccessor();
+                       
+                       // Tag type changes, invalidate old accessor
+                       if (sa!=null && !hadSameTag) {
+                               component = null;
+                               sa.invalidatedNotification();
+                       }
+                       
+                       // Tag type remains the same
+                       if (sa!=null && hadSameTag) {
+                               sa.setValue(cb, cv);
+                               return;
+                       }
+                       
+                       // Write
+                       UnionType ut = type();
+                       Datatype ct = ut.getComponent(tag).type;
+                       if (!ct.equals(cb.type())) {
+                               throw new AccessorException("Binding of "+ct+" expected.");
+                       }
+                       TObjectIntHashMap<Object> ids = new TObjectIntHashMap<Object>(0);
+                       int len = Endian.getUIntLength(count()-1);
+                       
+                       Serializer s = params.serializerScheme.getSerializer( cb );
+                       len += s.getSize(cv, ids);
+                       ids.clear();
+                       
+                       b.setLength(len);
+                       b.position(0L);
+                       Endian.putUInt(b, tag, count()-1);
+                       s.serialize(b, ids, cv);
+                       
+                       // Notify Listeners
+                       ListenerEntry le = listeners;
+                       while (le!=null) {                              
+                               UnionInterestSet is = le.getInterestSet();
+                               if (is.inNotificationsOf(tag)) {
+                                       MutableVariant newComponentValue = null;
+                                       if (is.inValuesOf(tag)) newComponentValue = new MutableVariant(cb, cb.isImmutable() ? cv : cb.clone(cv));
+                                       UnionValueAssigned e = new UnionValueAssigned(newTag, newComponentValue);
+                                       emitEvent(le, e);
+                               }
+                               
+                               // Attach component listener
+//                             InterestSet cis = is.getComponentInterest(newTag);
+//                             if (cis!=null && getExistingAccessor()==null) {
+//                                     sa = getComponentAccessor();
+//                             }
+                               
+                               le = le.next;
+                       }
+                       
+               } catch (IOException e) {
+                       throw new AccessorException(e);
+               } catch (AdaptException e) {
+                       throw new AccessorException(e);
+               } catch (SerializerConstructionException e) {\r
+                       throw new AccessorException(e);\r
+               } finally {\r
+                       writeUnlock();\r
+               }
+               
+       }
+
+       @Override
+       public void addListener(Listener listener, InterestSet interestSet,
+                       ChildReference path, Executor executor) throws AccessorException {
+               super.addListener(listener, interestSet, path, executor);
+               UnionInterestSet is = (UnionInterestSet) interestSet;           
+               if (is.componentInterests!=null) {
+                       int tag = getTag();
+                       InterestSet cis = is.componentInterests[tag];
+                       if (cis==null) return;
+                       BinaryObject sa = getExistingAccessor();
+                       if (sa==null) return;
+                       ChildReference childPath = ChildReference.concatenate(path, new IndexReference(tag) );
+                       sa.addListener(listener, cis, childPath, executor);
+               }
+       }       
+       
+       @Override
+       public void removeListener(Listener listener) throws AccessorException {
+               ListenerEntry e = detachListener(listener);
+               if (e==null) return;
+               UnionInterestSet is = (UnionInterestSet) e.interestSet;
+               
+               if (is.componentInterests!=null) {
+                       for (int i=0; i<is.componentInterests.length; i++) {
+                               InterestSet cis = is.componentInterests[i];
+                               if (cis==null) continue;
+                               BinaryObject sa = getExistingAccessor();
+                               if (sa==null) return;
+                               sa.removeListener(listener);
+                       }
+               }
+       }
+       
+       @Override
+       Event applyLocal(Event e, boolean makeRollback) throws AccessorException {
+               Event rollback = null;\r
+               if (makeRollback) {\r
+                       try {\r
+                               UnionType ut = type();\r
+                               int tag = getTag();\r
+                               Datatype ct = ut.getComponent(tag).type;\r
+                               Binding cb = params.bindingScheme.getBinding(ct);\r
+                               Object cv = getComponentValue(cb);\r
+                               MutableVariant v = new MutableVariant(cb, cv);\r
+                               rollback = new UnionValueAssigned(tag, v);\r
+                       } catch (BindingConstructionException e2) {\r
+                               throw new AccessorException( e2 );\r
+                       }\r
+               }\r
+               \r
+               if (e instanceof ValueAssigned) {\r
+                       ValueAssigned va = (ValueAssigned) e;\r
+                       setValueNoflush(va.newValue.getBinding(), va.newValue.getValue());\r
+                       return rollback;\r
+               } else  \r
+               if (e instanceof UnionValueAssigned) {          
+                       UnionValueAssigned ua = (UnionValueAssigned) e;
+                       if (ua.tag<0 || ua.tag>=type().getComponentCount()) throw new AccessorException("Tag index ("+ua.tag+") out of bounds.");
+                       if (!ua.newValue.type().equals( type().getComponent(ua.tag).type ) )
+                                       throw new AccessorException("Cannot assign "+ua.newValue.type()+" to "+type().getComponent(ua.tag).type);
+       
+                       setComponentValueNoflush(ua.tag, ua.newValue.getBinding(), ua.newValue.getValue());             
+                       return rollback;\r
+               } else {\r
+                       throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type");\r
+               }
+       }
+       
+}
+