/******************************************************************************* * 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.java; import java.lang.ref.SoftReference; 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.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.adapter.AdapterConstructionException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.UnionBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.UnionType; /** * Accessor to Java Object of Union Type * * @author Toni Kalajainen */ public class JavaUnion extends JavaObject implements UnionAccessor { /** Accessor to childr */ SoftReference component; public JavaUnion(Accessor parent, UnionBinding binding, Object object, AccessorParams params) { super(parent, binding, object, params); } @Override public UnionType type() { return (UnionType) binding.type(); } @Override public UnionBinding getBinding() { return (UnionBinding) binding; } @SuppressWarnings("unchecked") @Override public T getComponent(ChildReference reference) throws AccessorConstructionException { 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 by name \""+nr.name+"\" is 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 UnionType"); } catch (AccessorException ae) { throw new AccessorConstructionException(ae); } } public int count() throws AccessorException { return type().getComponentCount(); }; /** * Get existing sub-accessor * * @return sub-accessor or null */ JavaObject getExistingAccessor() { SoftReference c = component; if (c==null) return null; return c.get(); } @Override public void setValue(Binding unionBinding, Object newUnionValue) throws AccessorException { writeLock(); try { UnionBinding newUb = (UnionBinding) unionBinding; int tag = newUb.getTag(newUnionValue); Binding cb = newUb.getComponentBinding(tag); Object cv = newUb.getValue(newUnionValue); setComponentValue(tag, cb, cv); } catch (BindingException e) { throw new AccessorException(e); } finally { writeUnlock(); } } @SuppressWarnings("unchecked") @Override public T getComponentAccessor() throws AccessorConstructionException { try { // Get existing or create new JavaObject sa = getExistingAccessor(); if (sa==null) { readLock(); try { // Instantiate new accessor int tag = getBinding().getTag(object); Binding cb = getBinding().getComponentBinding(tag); Object cv = getBinding().getValue(object); // Instantiate correct sub accessor. sa = createSubAccessor(this, cb, cv, 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; } } finally { readUnlock(); } } return (T) sa; } catch (BindingException be) { throw new AccessorConstructionException( be ); } } @Override public Object getComponentValue(Binding componentBinding) throws AccessorException { readLock(); try { int tag = getBinding().getTag(object); Binding cb = getBinding().getComponentBinding(tag); Object cv = getBinding().getValue(object); return adapt(cv, cb, componentBinding); } catch (AdaptException ae) { throw new AccessorException( ae ); } catch (BindingException be) { throw new AccessorException( be ); } catch (AdapterConstructionException e) { throw new AccessorException( e ); } finally { readUnlock(); } } @Override public int getTag() throws AccessorException { readLock(); try { return getBinding().getTag(object); } catch (BindingException e) { throw new AccessorException(); } finally { readUnlock(); } } @Override public void setComponentValue(int tag, Binding componentBinding, Object componentValue) throws AccessorException { writeLock(); try { int oldTag = getBinding().getTag(object); int newTag = tag; boolean hadSameTag = oldTag == newTag; // Write to component JavaObject 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(componentBinding, componentValue); return; } // Write Binding cb = getBinding().getComponentBinding(newTag); Object cv = adapt(componentValue, componentBinding, cb); getBinding().setValue(object, newTag, 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 (BindingException e) { throw new AccessorException(e); } catch (AdaptException e) { throw new AccessorException(e); } catch (AdapterConstructionException 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; JavaObject 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); if (makeRollback) { UnionBinding ub = getBinding(); int tag = getTag(); Binding cb = ub.getComponentBinding(tag); Object cv = ub.getValue(object); MutableVariant v = new MutableVariant(cb, cv); rollback = new UnionValueAssigned(tag, v); } setComponentValue(ua.tag, ua.newValue.getBinding(), ua.newValue.getValue()); return rollback; } else { throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Union Type"); } } catch (BindingException be) { throw new AccessorException(be); } } }