/******************************************************************************* * 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.RecordAccessor; 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.RecordInterestSet; import org.simantics.databoard.accessor.reference.ChildReference; 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.RecordBinding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.RecordType; /** * Accessor to a Java Object of Record Type. * * @author Toni Kalajainen */ public class JavaRecord extends JavaObject implements RecordAccessor { /** Accessors to children */ SoftReference[] children; @SuppressWarnings("unchecked") public JavaRecord(Accessor parent, RecordBinding binding, Object object, AccessorParams params) throws AccessorConstructionException { super(parent, binding, object, params); RecordType type = (RecordType) binding.type(); if (type.isReferable()) throw new AccessorConstructionException("Refereable record are not supported"); int count = binding.type().getComponentCount(); children = new SoftReference[count]; } @Override public RecordBinding getBinding() { return (RecordBinding) binding; } @Override public RecordType type() { return (RecordType) getBinding().type(); } @Override public int count() { return type().getComponentCount(); } @SuppressWarnings("unchecked") @Override public T getFieldAccessor(String fieldName) throws AccessorConstructionException { int fieldIndex = type().getComponentIndex(fieldName); if (fieldIndex<0) throw new AccessorConstructionException("Field "+fieldName+" does not exist"); return (T) getFieldAccessor(fieldIndex); } @SuppressWarnings("unchecked") @Override public T getFieldAccessor(int index) throws AccessorConstructionException { if (index<0 || index>=count()) throw new ReferenceException("Field index ("+index+") out of bounds ("+count()+")"); readLock(); try { // Get existing or create new SoftReference ref = children[index]; JavaObject sa = (ref!=null)?(JavaObject)ref.get():null; if (sa==null) { // Instantiate new accessor Binding cb = getBinding().getComponentBindings()[index]; Object cv = getBinding().getComponent(object, index); sa = createSubAccessor(this, cb, cv, params); sa.keyInParent = index; children[index] = new SoftReference(sa); // Add component interest sets ListenerEntry le = listeners; while (le!=null) { RecordInterestSet is = le.getInterestSet(); InterestSet cis = is.getComponentInterest(index); if (cis != null) { try { ChildReference childPath = ChildReference.concatenate( le.path, new IndexReference(index) ); 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); } finally { readUnlock(); } } /** * Get existing sub accessor * @param index * @return sub-accessor or null */ JavaObject getExistingAccessor(int index) { if (index<0 || index>=count()) throw new RuntimeException("Field index ("+index+") out of bounds ("+count()+")"); // Get existing or create new SoftReference ref = children[index]; JavaObject accessor = (ref!=null)?(JavaObject)ref.get():null; return accessor; } @Override public Object getFieldValue(String fieldName, Binding fieldBinding) throws AccessorException { int fieldIndex = type().getComponentIndex(fieldName); if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist"); return getFieldValue(fieldIndex, fieldBinding); } @Override public Object getFieldValue(int index, Binding fieldBinding) throws AccessorException { readLock(); try { Object cv = getBinding().getComponent(object, index); Binding cb = getBinding().getComponentBindings()[index]; return adapt(cv, cb, fieldBinding); } catch (BindingException e) { throw new AccessorException(e); } catch (AdaptException e) { throw new AccessorException(e); } catch (AdapterConstructionException e) { throw new AccessorException(e); } finally { readUnlock(); } } public void setFieldValue(String fieldName, Binding fieldBinding, Object value) throws AccessorException { int fieldIndex = type().getComponentIndex(fieldName); if (fieldIndex<0) throw new AccessorException("Field "+fieldName+" does not exist"); setFieldValue(fieldIndex, fieldBinding, value); }; @Override public void setFieldValue(int index, Binding binding, Object value) throws AccessorException { writeLock(); try { JavaObject sa = getExistingAccessor(index); if (sa==null) { // Init Binding lb = getBinding().getComponentBindings()[index]; Binding rb = binding; //Object oldlv = getBinding().getComponent(object, index); Object rv = value; // Compare to existing value // boolean equal = Bindings.equals(lb, oldlv, lb, rv); // if (equal) return; // Write Object lv = adapt(rv, rb, lb); getBinding().setComponent(object, index, lv); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { RecordInterestSet is = le.getInterestSet(); if (is.inNotificationsOf(index)) { MutableVariant newValue = is.inValuesOf(index) ? new MutableVariant(binding, value) : null; if (is.inValuesOf(index)) newValue = new MutableVariant(binding, binding.isImmutable() ? value : binding.clone(value)); Event e = new ValueAssigned(new IndexReference(index), newValue); emitEvent(le, e); } le = le.next; } } else { // Recursive write using existing sub accessor sa.setValue(binding, value); } } 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 { RecordInterestSet is = (RecordInterestSet) interestSet; super.addListener(listener, interestSet, path, executor); // Apply component interest set to existing sub-accessors if (is.componentInterests!=null) { for (int i=0; i T getComponent(ChildReference reference) throws AccessorConstructionException { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; String fieldName = lr.label; Integer index = type().getComponentIndex(fieldName); if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\""); JavaObject sa = getFieldAccessor(index); if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } if (reference instanceof IndexReference) { IndexReference ref = (IndexReference) reference; int index = ref.getIndex(); JavaObject sa = getFieldAccessor(index); if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } if (reference instanceof NameReference) { NameReference ref = (NameReference) reference; String fieldName = ref.getName(); Integer index = type().getComponentIndex(fieldName); if (index==null) throw new ReferenceException("RecordType doesn't have field by name \""+fieldName+"\""); JavaObject sa = getFieldAccessor(index); if (reference.getChildReference() != null) sa = sa.getComponent(reference.getChildReference()); return (T) sa; } throw new ReferenceException(reference.getClass()+" is not a subreference of RecordType"); } @Override public void setValue(Binding binding, Object newValue) throws AccessorException { writeLock(); try { RecordBinding rb = (RecordBinding) binding; for (int i=0; i