--- /dev/null
+/*******************************************************************************\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
+ }
+ }
+
+}
+