/******************************************************************************* * 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.OptionalAccessor; 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.OptionalValueAssigned; import org.simantics.databoard.accessor.event.OptionalValueRemoved; 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.OptionalInterestSet; 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.adapter.AdapterConstructionException; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.OptionalBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.OptionalType; public class JavaOptional extends JavaObject implements OptionalAccessor { /** Accessor to component */ SoftReference component; public JavaOptional(Accessor parent, OptionalBinding binding, Object object, AccessorParams params) { super(parent, binding, object, params); } @Override public OptionalBinding getBinding() { return (OptionalBinding) binding; } @Override public OptionalType type() { return (OptionalType) getBinding().type(); } @SuppressWarnings("unchecked") @Override public T getComponentAccessor() throws AccessorConstructionException { try { if (!getBinding().hasValue(object)) return null; // Get existing or create new JavaObject sa = getExistingAccessor(); if (sa==null) { // Instantiate new accessor Binding cb = getBinding().getComponentBinding(); 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) { OptionalInterestSet 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); } } le = le.next; } } return (T) sa; } catch (BindingException e) { throw new AccessorConstructionException(e); } } /** * Get existing sub accessor * * @return sub-accessor or null */ JavaObject getExistingAccessor() { // Get existing or create new SoftReference ref = component; JavaObject accessor = (ref!=null)?(JavaObject)ref.get():null; return accessor; } @Override public Object getComponentValue(Binding componentBinding) throws AccessorException { readLock(); try { if (!getBinding().hasValue(object)) throw new AccessorException("There is no component value"); Binding cb = getBinding().getComponentBinding(); Object cv = getBinding().getValue(object); return adapt(cv, cb, componentBinding); } catch (BindingException e) { throw new AccessorException(e); } catch (AdaptException e) { throw new AccessorException(e); } catch (AdapterConstructionException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public void addListener(Listener listener, InterestSet interestSet, ChildReference path, Executor executor) throws AccessorException { super.addListener(listener, interestSet, path, executor); OptionalInterestSet is = (OptionalInterestSet) interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null) return; JavaObject sa = getExistingAccessor(); if (sa==null) return; ChildReference childPath = ChildReference.concatenate( path, new ComponentReference() ); sa.addListener(listener, cis, childPath, executor); } @Override public void removeListener(Listener listener) throws AccessorException { ListenerEntry e = detachListener(listener); if (e==null) return; OptionalInterestSet is = (OptionalInterestSet) e.interestSet; InterestSet cis = is.getComponentInterest(); if (cis==null) return; JavaObject sa = getExistingAccessor(); if (sa==null) return; sa.removeListener(listener); } @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("o")) { 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; } throw new ReferenceException(reference.getClass()+" is not a reference of OptionalType"); } @Override public void setValue(Binding binding, Object newValue) throws AccessorException { writeLock(); try { OptionalBinding rb = (OptionalBinding) binding; Object rv = newValue; boolean rvHasValue = rb.hasValue(newValue); if (rvHasValue) { Binding rcb = rb.getComponentBinding(); Object rcv = rb.getValue(rv); setComponentValue(rcb, rcv); } else { setNoValue(); } } catch (BindingException e) { throw new AccessorException(e); } finally { writeUnlock(); } } @Override public boolean hasValue() throws AccessorException { readLock(); try { return getBinding().hasValue(object); } catch (BindingException e) { throw new AccessorException(e); } finally { readUnlock(); } } @Override public void setNoValue() throws AccessorException { writeLock(); try { boolean hadValue = getBinding().hasValue(object); if (!hadValue) return; // Write getBinding().setNoValue(object); // Disconnect sub-accessor JavaObject sa = getExistingAccessor(); component = null; // Notify about disconnection of sub-accessor if (sa!=null) sa.invalidatedNotification(); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { OptionalInterestSet is = le.getInterestSet(); if (is.inNotifications()) { OptionalValueRemoved e = new OptionalValueRemoved(); emitEvent(le, e); } le = le.next; } } catch (BindingException e) { throw new AccessorException(e); } finally { writeUnlock(); } } @Override public void setComponentValue(Binding binding, Object value) throws AccessorException { writeLock(); try { Binding rcb = binding; Object rcv = value; boolean hadValue = getBinding().hasValue(object); // Write to component JavaObject sa = getExistingAccessor(); if (sa!=null) { assert(hadValue); sa.setValue(rcb, rcv); return; } Binding lcb = getBinding().getComponentBinding(); Object lcv = adapt(rcv, rcb, lcb); // Write getBinding().setValue(object, lcv); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { OptionalInterestSet is = le.getInterestSet(); if (is.inNotifications()) { MutableVariant newComponentValue = null; if (is.inValues()) newComponentValue = new MutableVariant(lcb, lcb.isImmutable() ? lcv : lcb.clone(lcv)); OptionalValueAssigned e = new OptionalValueAssigned(newComponentValue); emitEvent(le, e); } // Attach component interest // if (is.getComponentInterest()!=null && getComponentAccessor()!=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 Event applyLocal(Event e, boolean makeRollback) throws AccessorException { try { Event rollback = null; if (makeRollback) { OptionalBinding b = getBinding(); Binding cb = getBinding().getComponentBinding(); rollback = hasValue() ? new OptionalValueAssigned(cb, b.getValue(object)) : new OptionalValueRemoved(); } if (e instanceof ValueAssigned) { ValueAssigned va = (ValueAssigned) e; setValue(va.newValue.getBinding(), va.newValue.getValue()); return rollback; } else if (e instanceof OptionalValueAssigned) { OptionalValueAssigned oa = (OptionalValueAssigned) e; if (oa.newValue==null) throw new AccessorException("Cannot apply field assignment event, the value is missing"); setComponentValue(oa.newValue.getBinding(), oa.newValue.getValue()); } else if (e instanceof OptionalValueRemoved) { //OptionalValueRemoved or = (OptionalValueRemoved) e; setNoValue(); } else throw new AccessorException("Unexpected event type "+e.getClass().getName()+" for an Optional Type"); return rollback; } catch (BindingException be) { throw new AccessorException( be ); } } }