/******************************************************************************* * 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.Bindings; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.VariantAccessor; 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.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.VariantInterestSet; import org.simantics.databoard.accessor.reference.ChildReference; import org.simantics.databoard.accessor.reference.ComponentReference; import org.simantics.databoard.accessor.reference.LabelReference; import org.simantics.databoard.adapter.AdaptException; import org.simantics.databoard.binding.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.RecordBinding; import org.simantics.databoard.binding.VariantBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.Datatype; import org.simantics.databoard.type.VariantType; public class JavaVariant extends JavaObject implements VariantAccessor { SoftReference child; public JavaVariant(Accessor parent, VariantBinding binding, Object object, AccessorParams params) { super(parent, binding, object, params); } @Override public VariantType type() { return (VariantType) binding.type(); } @Override public VariantBinding getBinding() { return (VariantBinding) binding; } @SuppressWarnings("unchecked") @Override public T getContentAccessor() throws AccessorConstructionException { try { JavaObject sa = getExistingAccessor(); if (sa==null) { readLock(); try { // Create new child Binding cb = getBinding().getContentBinding(object); Object cv = getBinding().getContent(object); sa = JavaObject.createSubAccessor(this, cb, cv, params); child = new SoftReference(sa); // Attach listeners of interest set to the new accessor (of the value) ListenerEntry le = listeners; while (le!=null) { VariantInterestSet is = le.getInterestSet(); InterestSet cis = is.getComponentInterest(); 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); } } if (is.inCompleteComponent()) { cis = InterestSet.newInterestSet(getContentType(), true, true, true); 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 e) { throw new AccessorConstructionException(e); } catch (AccessorException e) { throw new AccessorConstructionException(e); } } @SuppressWarnings("unchecked") @Override public T getComponent(ChildReference reference) throws AccessorConstructionException { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; if (lr.label.equals("v")) { Accessor sa = getContentAccessor(); if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } } if (reference instanceof ComponentReference) { Accessor sa = getContentAccessor(); if (reference.getChildReference()!=null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } throw new ReferenceException("Variant value reference expected, got "+reference.getClass().getName()); } protected JavaObject getExistingAccessor() { SoftReference r = child; if (r==null) return null; JavaObject sa = r.get(); return sa; } @Override public void setValue(Binding variantBinding, Object newVariant) throws AccessorException { writeLock(); try { if (binding instanceof VariantBinding == false) throw new AccessorException("VariantBinding is expected."); Binding newValueBinding = ((VariantBinding)variantBinding).getContentBinding(newVariant); Object newValueObject = ((VariantBinding)variantBinding).getContent(newVariant); setContentValue(newValueBinding, newValueObject); } catch (BindingException e) { throw new AccessorException( e ); } finally { writeUnlock(); } } @Override public Object getContentValue(Binding contentBinding) throws AccessorException { readLock(); try { return getBinding().getContent(object); } catch (BindingException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public Datatype getContentType() throws AccessorException { readLock(); try { return getBinding().getContentType(object); } catch (BindingException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public void setContentValue(Binding valueBinding, Object newValue) throws AccessorException { writeLock(); try { // reuse sub-accessor, if type matches, and sub-accessor exists JavaObject sa = getExistingAccessor(); if (sa!=null && valueBinding.type().equals(sa.type())) { sa.setValue(valueBinding, newValue); return; } { // Replace the old value with a new value if (sa!=null) { sa.invalidatedNotification(); child = null; sa = null; } if (binding.isImmutable() && parent!=null && parent instanceof JavaArray) { JavaObject jo = (JavaObject) parent; ArrayBinding ab = (ArrayBinding) jo.binding; Object nv = getBinding().create(valueBinding, newValue); ab.set(jo.object, (Integer)keyInParent, nv); this.object = nv; } else if (binding.isImmutable() && parent!=null && parent instanceof JavaRecord) { JavaObject jo = (JavaObject) parent; RecordBinding rb = (RecordBinding) jo.binding; Object nv = getBinding().create(valueBinding, newValue); rb.setComponent(jo.object, (Integer)keyInParent, nv); this.object = nv; } else if (binding.isImmutable() && parent!=null && parent instanceof JavaVariant) { JavaObject jo = (JavaObject) parent; VariantBinding vb = (VariantBinding) jo.binding; Object nv = getBinding().create(valueBinding, newValue); vb.setContent(jo.object, getBinding(), nv); this.object = nv; } else { // Write new value getBinding().setContent(object, valueBinding, newValue); } // Notify Listeners ListenerEntry le = listeners; while (le!=null) { // Notification VariantInterestSet is = le.getInterestSet(); if (is.inNotifications()) { if (is.inValues()) { MutableVariant value = new MutableVariant( valueBinding, valueBinding.isImmutable() ? newValue : valueBinding.clone(newValue) ); ValueAssigned e = new ValueAssigned(Bindings.MUTABLE_VARIANT, value ); emitEvent(le, e); } else { emitEvent( le, new ValueAssigned() ); } } // Attach component listener // InterestSet cis = is.getComponentInterest(); // if (cis!=null) { // sa = getValueAccessor(); // } le = le.next; } } } catch (BindingException e) { throw new AccessorException(e); // } catch (AccessorConstructionException e) { // throw new AccessorException(e); } catch (AdaptException 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); VariantInterestSet is = (VariantInterestSet) interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null && !is.inCompleteComponent()) return; JavaObject sa = getExistingAccessor(); if (sa==null) return; if (cis!=null) { ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() ); sa.addListener(listener, cis, childPath, executor); } if (is.inCompleteComponent()) { ChildReference childPath = ChildReference.concatenate(path, new ComponentReference() ); cis = InterestSet.newInterestSet(getContentType(), true, true, true); sa.addListener(listener, cis, childPath, executor); } } @Override public void removeListener(Listener listener) throws AccessorException { ListenerEntry e = detachListener(listener); if (e==null) return; VariantInterestSet is = (VariantInterestSet) e.interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null && !is.inCompleteComponent()) return; JavaObject sa = getExistingAccessor(); if (sa==null) return; if (cis!=null) { sa.removeListener(listener); } // Remove another? if (is.inCompleteComponent()) { sa.removeListener(listener); } } @Override Event applyLocal(Event e, boolean makeRollback) throws AccessorException { try { Event rollback = null; if (e instanceof ValueAssigned) { ValueAssigned va = (ValueAssigned) e; if (va.newValue==null) throw new AccessorException("Cannot apply variant assignment event, the value is missing"); if (makeRollback) { Binding cb = getBinding().getContentBinding(object); Object cv = getBinding().getContent(object); rollback = new ValueAssigned( Bindings.MUTABLE_VARIANT, new MutableVariant( cb, cv ) ); } setValue(va.newValue.getBinding(), va.newValue.getValue()); // setContentValue(va.newValue.getBinding(), va.newValue.getValue()); return rollback; } else { throw new AccessorException("Invalid event "+e.getClass().getName()); } } catch (BindingException be) { throw new AccessorException(be); } } }