/******************************************************************************* * Copyright (c) 2010 Association for Decentralized Information Management in * Industry THTH ry. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.databoard.accessor.binary; import gnu.trove.map.hash.TObjectIntHashMap; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.UnionAccessor; import org.simantics.databoard.accessor.error.AccessorConstructionException; import org.simantics.databoard.accessor.error.AccessorException; import org.simantics.databoard.accessor.error.ReferenceException; import org.simantics.databoard.accessor.event.Event; import org.simantics.databoard.accessor.event.UnionValueAssigned; import org.simantics.databoard.accessor.event.ValueAssigned; import org.simantics.databoard.accessor.file.FileUnionAccessor; import org.simantics.databoard.accessor.impl.AccessorParams; import org.simantics.databoard.accessor.impl.ListenerEntry; import org.simantics.databoard.accessor.interestset.InterestSet; import org.simantics.databoard.accessor.interestset.UnionInterestSet; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.ComponentReference; import org.simantics.databoard.accessor.reference.IndexReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.accessor.reference.NameReference; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.error.BindingConstructionException; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.serialization.Serializer; import org.simantics.databoard.serialization.SerializerConstructionException; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.UnionType; import org.simantics.databoard.util.binary.Blob; import org.simantics.databoard.util.binary.Endian; public class BinaryUnion extends BinaryObject implements UnionAccessor, FileUnionAccessor { /** Accessor to childr */ SoftReference 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 { assert b.isOpen(); 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 { writeUnlock(); } } @SuppressWarnings("unchecked") @Override public T getComponentAccessor() throws AccessorConstructionException { assert b.isOpen(); 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(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 { readUnlock(); } } /** * Get existing sub-accessor * * @return sub-accessor or null */ BinaryObject getExistingAccessor() { SoftReference c = component; if (c==null) return null; return c.get(); } @Override public Object getComponentValue(Binding componentBinding) throws AccessorException { assert b.isOpen(); 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 ids = new ArrayList(0); Serializer s = params.serializerScheme.getSerializer( componentBinding ); return s.deserialize(b, ids); } catch (IOException e) { throw new AccessorException(e); } catch (SerializerConstructionException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public int getTag() throws AccessorException { assert b.isOpen(); readLock(); try { b.position(0L); return Endian.getUInt(b, count()-1); } catch (IOException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public void setComponentValue(int tag, Binding componentBinding, Object componentValue) throws AccessorException { assert b.isOpen(); writeLock(); try { setComponentValueNoflush(tag, componentBinding, componentValue); flush(); } finally { writeUnlock(); } } @SuppressWarnings("unchecked") @Override public T getComponent(ChildReference reference) throws AccessorConstructionException { assert b.isOpen(); readLock(); try { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; Integer tag = type().getComponentIndex( lr.label ); if (tag==null && lr.label.equals("uv")) { Accessor result = getComponentAccessor(); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } else if (tag==null) { throw new ReferenceException("Tag \""+lr.label+"\" 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; } if (reference instanceof ComponentReference) { Accessor result = getComponentAccessor(); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } 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; } 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; } throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType"); } catch (AccessorException ae) { throw new AccessorConstructionException(ae); } finally { readUnlock(); } } @Override public void setComponentValueNoflush(int tag, Binding cb, Object cv) throws AccessorException { assert b.isOpen(); 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 ids = new TObjectIntHashMap(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) { throw new AccessorException(e); } finally { writeUnlock(); } } @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=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; } else { throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type"); } } }