/******************************************************************************* * 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.io.File; import java.lang.ref.SoftReference; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.Executor; import org.simantics.databoard.accessor.Accessor; import org.simantics.databoard.accessor.ArrayAccessor; import org.simantics.databoard.accessor.StreamAccessor; 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.ArrayElementAdded; import org.simantics.databoard.accessor.event.ArrayElementRemoved; 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.ArrayInterestSet; import org.simantics.databoard.accessor.interestset.InterestSet; 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.adapter.AdaptException; import org.simantics.databoard.adapter.Adapter; import org.simantics.databoard.adapter.AdapterConstructionException; import org.simantics.databoard.binding.ArrayBinding; import org.simantics.databoard.binding.Binding; import org.simantics.databoard.binding.error.BindingException; import org.simantics.databoard.binding.mutable.MutableVariant; import org.simantics.databoard.type.ArrayType; public class JavaArray extends JavaObject implements ArrayAccessor, StreamAccessor { /** Accessors to children */ TreeMap> children = new TreeMap>(); public JavaArray(Accessor parent, ArrayBinding binding, Object object, AccessorParams params) { super(parent, binding, object, params); } @Override public ArrayBinding getBinding() { return (ArrayBinding) binding; } @Override public ArrayType type() { return (ArrayType) getBinding().type(); } @Override public void add(Binding binding, Object value) throws AccessorException { add(size(), binding, value); } @Override public void addAll(Binding binding, Object[] values) throws AccessorException { addAll(size(), binding, values); } @Override public void add(int index, Binding binding, Object value) throws AccessorException { writeLock(); try { boolean lastEntry = index==size(); // Write Binding rcb = binding; Binding lcb = getBinding().getComponentBinding(); Object rcv = value; Object lcv = params.adapterScheme.clone(rcv, rcb, lcb); getBinding().add(object, index, lcv); // Update child map keys if (!lastEntry && !children.isEmpty()) { Integer key = children.lastKey(); while (key != null && key >= index) { SoftReference v = children.remove(key); if (v.get()!=null) children.put(key+1, v); key = children.lowerKey(key); } } // Notify Listeners ListenerEntry le = listeners; while (le!=null) { ArrayInterestSet is = le.getInterestSet(); if (is.inNotifications()) { MutableVariant newValue = null; if (is.inValues()) newValue = new MutableVariant(lcb, lcb.isImmutable() ? lcv : lcb.clone(lcv)); ArrayElementAdded e = new ArrayElementAdded(index, newValue); emitEvent(le, e); } // Update indices of interest sets if (is.componentInterests!=null) { Map oldCis = is.componentInterests; boolean needUpdates = false; for (Integer i : oldCis.keySet()) { needUpdates |= i>=index; if (needUpdates) break; } if (needUpdates) { Map newCis = new HashMap(oldCis.size()); for (Integer i : oldCis.keySet()) { Integer oldKey = i; Integer newKey = i>=index ? i+1 : i; InterestSet oldValue = oldCis.get(oldKey); newCis.put(newKey, oldValue); } is.componentInterests = newCis; } } /* boolean hadSa = getExistingAccessor(index) != null; if (!hadSa) { // Add component interest listener InterestSet cis = is.getComponentInterest(); if (cis != null) { Accessor sa = getAccessor(index); } cis = is.getComponentInterest(index); if (cis != null) { Accessor sa = getAccessor(index); } } */ le = le.next; } } catch (IndexOutOfBoundsException e) { throw new AccessorException(e); } catch (BindingException e) { throw new AccessorException(e); } catch (AdaptException e) { throw new AccessorException(e); // } catch (AccessorConstructionException e) { // throw new AccessorException(e); } finally { writeUnlock(); } } @Override public void addAll(int index, Binding binding, Object[] values) throws AccessorException { writeLock(); try { int oldCount = size(); int count = values.length; Binding rcb = binding; Binding lcb = getBinding().getComponentBinding(); boolean lastEntry = index == oldCount; // Write All for (int i=0; i= index) { SoftReference value = children.remove(key); if (value.get()!=null) children.put(key+values.length, value); key = children.lowerKey(key); } } // Update indices of interest sets { ListenerEntry le = listeners; while (le!=null) { ArrayInterestSet is = le.getInterestSet(); if (is.componentInterests!=null) { Map oldCis = is.componentInterests; boolean needUpdates = false; for (Integer i : oldCis.keySet()) { needUpdates |= i>=index; if (needUpdates) break; } if (needUpdates) { Map newCis = new HashMap(oldCis.size()); for (Integer i : oldCis.keySet()) { Integer oldKey = i; Integer newKey = i>=index ? i+count : i; InterestSet oldValue = oldCis.get(oldKey); newCis.put(newKey, oldValue); } is.componentInterests = newCis; } } // Add component interest listener /* for (int i = index; i T getComponent(ChildReference reference) throws AccessorConstructionException { if (reference==null) return (T) this; if (reference instanceof LabelReference) { LabelReference lr = (LabelReference) reference; try { Integer index = new Integer( lr.label ); Accessor result = getAccessor(index); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } catch ( NumberFormatException nfe ) { throw new ReferenceException(nfe); } } else if (reference instanceof IndexReference) { IndexReference ref = (IndexReference) reference; int index = ref.getIndex(); Accessor result = getAccessor(index); if (reference.getChildReference() != null) result = result.getComponent(reference.getChildReference()); return (T) result; } throw new ReferenceException(reference.getClass().getName()+" is not a reference of an array"); } @SuppressWarnings("unchecked") @Override public T getAccessor(int index) throws AccessorConstructionException { try { int size = getBinding().size(object); if (index<0 || index>=size) throw new ReferenceException("Element index ("+index+") out of bounds ("+size+")"); // Get existing or create new JavaObject sa = getExistingAccessor(index); if (sa==null) { readLock(); try { // Instantiate new accessor Binding cb = getBinding().getComponentBinding(); Object cv = getBinding().get(object, index); // Instantiate correct sub accessor. sa = createSubAccessor(this, cb, cv, params); sa.keyInParent = index; children.put(index, new SoftReference(sa) ); // Add component interest sets ListenerEntry le = listeners; while (le!=null) { ArrayInterestSet is = le.getInterestSet(); // Generic element interest InterestSet gis = is.getComponentInterest(); if (gis != null) { try { ChildReference childPath = ChildReference.concatenate(le.path, new IndexReference(index) ); sa.addListener(le.listener, gis, childPath, le.executor); } catch (AccessorException e) { throw new AccessorConstructionException(e); } } // Specific element interest 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); } } // Next listener le = le.next; } } finally { readUnlock(); } } return (T) sa; } catch (BindingException e) { throw new AccessorConstructionException(e); } } /** * Get existing sub accessor * @param index * @return sub-accessor or null */ JavaObject getExistingAccessor(int index) { SoftReference ref = children.get(index); if (ref==null) return null; JavaObject res = (JavaObject) ref.get(); // if (res==null) children.remove(index); return res; } @Override public void getAll(Binding valueBinding, Collection values) throws AccessorException { readLock(); try { Adapter adapter = params.adapterScheme.getAdapter(getBinding().getComponentBinding(), valueBinding, true, true); for (int i=0; isize()) throw new AccessorException("Index out of bounds"); writeLock(); try { boolean lastEntry = index==size()-count; // Write getBinding().remove(object, index, count); // Disconnect sub-accessor JavaObject sa = getExistingAccessor(index); // Notify about disconnection of sub-accessor if (sa!=null) { sa.invalidatedNotification(); children.remove(index); sa = null; } // Remove children SortedMap> sm = children.subMap(index, true, index+count, false); for (Entry> e : sm.entrySet()) { JavaObject bo = e.getValue().get(); if (bo==null) continue; bo.invalidatedNotification(); } sm.clear(); // Update the keys of consecutive children if (!lastEntry && !children.isEmpty()) { Integer lastKey = children.lastKey(); Integer key = children.higherKey(index); while (key != null && key <= lastKey) { SoftReference value = children.remove(key); if (value.get()!=null) children.put(key-count, value); key = children.higherKey(key); } } // Notify Listeners ListenerEntry le = listeners; while (le!=null) { ArrayInterestSet is = le.getInterestSet(); if (is.inNotifications()) { for (int i=0; i oldCis = is.componentInterests; boolean needUpdates = false; for (Integer interestIndex : oldCis.keySet()) { needUpdates |= interestIndex>=index; if (needUpdates) break; } if (needUpdates) { Map newCis = new HashMap(oldCis.size()); for (Integer interestIndex : oldCis.keySet()) { // The component interest is removed if (interestIndex>=index && interestIndex=index ? interestIndex+count : interestIndex; InterestSet oldValue = oldCis.get(oldKey); newCis.put(newKey, oldValue); } is.componentInterests = newCis; } } le = le.next; } } catch (IndexOutOfBoundsException e) { throw new AccessorException(e); } catch (BindingException e) { throw new AccessorException(e); } finally { writeUnlock(); } } @Override public void set(int index, Binding binding, Object value) throws AccessorException { if (index<0 || index>=size()) throw new AccessorException("Index out of bounds"); writeLock(); try { JavaObject sa = getExistingAccessor(index); // Create sub-accessor, if there is an interest /* if (sa==null) { ListenerEntry le = listeners; while (le!=null) { // Update indices of interest sets ArrayInterestSet is = le.getInterestSet(); if (is.getComponentInterest()!=null || is.getComponentInterest(index)!=null) { sa = (JavaObject) getAccessor(index); break; } le = le.next; } }*/ // Write with sub-accessor if (sa!=null) { sa.setValue(binding, value); return; } // Write value Binding lcb = getBinding().getComponentBinding(); Binding rcb = binding; Object rcv = value; Object lcv = params.adapterScheme.clone(rcv, rcb, lcb); getBinding().set(object, index, lcv); // Notify Listeners ListenerEntry le = listeners; while (le!=null) { ArrayInterestSet is = le.getInterestSet(); if (is.inNotificationsOf(index)) { MutableVariant newValue = null; if (is.inValues()) newValue = new MutableVariant(lcb, lcb.isImmutable() ? lcv : lcb.clone(lcv)); Event e = new ValueAssigned(new IndexReference(index), newValue); emitEvent(le, e); } le = le.next; } } catch (AdaptException e) { throw new AccessorException(e); } catch (BindingException e) { throw new AccessorException(e); // } catch (AccessorConstructionException e) { // throw new AccessorException(e); } finally { writeUnlock(); } } @Override public void setValue(Binding arrayBinding, Object newArray) throws AccessorException { writeLock(); try { // Replace all elements ArrayBinding ab = ((ArrayBinding)arrayBinding); int newLength = ab.size(newArray); int oldLength = size(); // 1. Set int commonLength = Math.min(newLength, oldLength); Binding cb = ab.getComponentBinding(); for (int i=0; ioldLength) { for (int i=oldLength; ioldSize) { Binding c = getBinding().getComponentBinding(); int count = newSize-oldSize; Object[] arr = new Object[count]; for (int i=0; i=size()) throw new AccessorException("Array index out of bounds"); if (makeRollback) { Binding cb = getBinding().getComponentBinding(); Object cv = getBinding().get(object, ar.index); if (!cb.isImmutable()) cv = cb.clone(cv); rollback = new ArrayElementAdded(ar.index, new MutableVariant(cb, cv)); } remove(ar.index, 1); } else { throw new AccessorException("Cannot apply "+e.getClass().getName()+" to Array"); } return rollback; } catch (BindingException be) { throw new AccessorException( be ); } catch (AdaptException ae) { throw new AccessorException( ae ); } } @Override public void flush() throws AccessorException { } @Override public void close() throws AccessorException { } @Override public void reset() throws AccessorException { } @Override public void addNoflush(Binding binding, Object value) throws AccessorException { add(binding, value); } @Override public void addAllNoflush(Binding binding, Object[] values) throws AccessorException { addAll(binding, values); } @Override public void addAllNoflush(int index, Binding binding, Object[] values) throws AccessorException { addAll(index, binding, values); } @Override public void addNoflush(int index, Binding binding, Object value) throws AccessorException { add(index, binding, value); } @Override public void setValueNoflush(Binding binding, Object newValue) throws AccessorException { setValue(binding, newValue); } @Override public void setNoflush(int index, Binding binding, Object value) throws AccessorException { set(index, binding, value); } @Override public void removeNoflush(int index, int count) throws AccessorException { remove(index, count); } @Override public void setSizeNoflush(int newSize) throws AccessorException { setSize(newSize); } @Override public File file() { return null; } }