--- /dev/null
+/*******************************************************************************\r
+ * Copyright (c) 2010 Association for Decentralized Information Management in\r
+ * Industry THTH ry.\r
+ * All rights reserved. This program and the accompanying materials\r
+ * are made available under the terms of the Eclipse Public License v1.0\r
+ * which accompanies this distribution, and is available at\r
+ * http://www.eclipse.org/legal/epl-v10.html\r
+ *\r
+ * Contributors:\r
+ * VTT Technical Research Centre of Finland - initial API and implementation\r
+ *******************************************************************************/\r
+package org.simantics.databoard.binding;
+
+import java.util.Collection;\r
+import java.util.IdentityHashMap;\r
+import java.util.Iterator;\r
+import java.util.Set;\r
+\r
+import org.simantics.databoard.Bindings;\r
+import org.simantics.databoard.accessor.reference.ChildReference;\r
+import org.simantics.databoard.accessor.reference.IndexReference;\r
+import org.simantics.databoard.accessor.reference.LabelReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.adapter.Adapter;\r
+import org.simantics.databoard.adapter.AdapterConstructionException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.impl.ArrayListBinding;\r
+import org.simantics.databoard.binding.impl.BindingPrintContext;\r
+import org.simantics.databoard.binding.impl.ByteArrayBinding;\r
+import org.simantics.databoard.binding.impl.DoubleArrayBinding;\r
+import org.simantics.databoard.binding.impl.FloatArrayBinding;\r
+import org.simantics.databoard.binding.impl.IntArrayBinding;\r
+import org.simantics.databoard.binding.impl.LinkedListBinding;\r
+import org.simantics.databoard.binding.impl.LongArrayBinding;\r
+import org.simantics.databoard.binding.impl.ObjectArrayBinding;\r
+import org.simantics.databoard.type.ArrayType;\r
+import org.simantics.databoard.util.IdentityPair;\r
+import org.simantics.databoard.util.Range;\r
+
+/**
+ * This is a binding of Array type and a Java Object.
+ *
+ * @see ArrayType\r
+ * @see ArrayListBinding for Binding of java.util.ArrayList<?>\r
+ * @see LinkedListBinding for Binding of java.util.LinkedList<?>\r
+ * @see ObjectArrayBinding for Binding of Object[]\r
+ * @see IntArrayBinding for primitive array int[] binding\r
+ * @see ByteArrayBinding for primitive array byte[] binding\r
+ * @see LongArrayBinding for primitive array long[] binding\r
+ * @see DoubleArrayBinding for primitive array double[] binding\r
+ * @see FloatArrayBinding for primitive array float[] binding \r
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public abstract class ArrayBinding extends Binding {
+
+ public Binding componentBinding;
+
+ public ArrayBinding(ArrayType type, Binding componentBinding) {
+// if (!type.getComponentType().equals(componentBinding.type()))
+// throw new IllegalArgumentException("Binding for "+type.componentType+" expected, got "+componentBinding.type());
+ this.componentBinding = componentBinding;
+ this.type = type;
+ }
+ \r
+ @Override\r
+ public Binding getComponentBinding(ChildReference path) {\r
+ if (path==null) return this; \r
+ if (path instanceof org.simantics.databoard.accessor.reference.ComponentReference) {\r
+ return componentBinding.getComponentBinding(path.childReference);\r
+ }\r
+ if (path instanceof IndexReference) {\r
+ IndexReference ir = (IndexReference) path; \r
+ if (ir.index == 0) { \r
+ return componentBinding.getComponentBinding(path.childReference);\r
+ }\r
+ }\r
+ if (path instanceof LabelReference) {\r
+ LabelReference lr = (LabelReference) path;\r
+ if (lr.label.equals("v")) { \r
+ return componentBinding.getComponentBinding(path.childReference);\r
+ }\r
+ }\r
+ throw new IllegalArgumentException();\r
+ }\r
+
+ /**
+ * Returns true if array length can be modified.
+ *
+ * @return true if array length can be modified, false if not
+ */
+ @Override
+ public boolean isImmutable() {
+ return super.isImmutable();
+ }\r
+ \r
+ /**\r
+ * Return true if the array's size can be adjusted and false if not\r
+ * \r
+ * @return true if array is resizable\r
+ */\r
+ public abstract boolean isResizable();\r
+
+ @Override
+ public ArrayType type() {
+ return (ArrayType) type;
+ }
+
+ public Binding getComponentBinding() {
+ return componentBinding;
+ }
+
+ /**
+ * Create a new empty array
+ * @return array object
+ */
+ public abstract Object create();
+
+ /**
+ * Create a new array with initial values copied or referred from a collection.
+ *
+ * @param collection
+ * @return array object
+ * @throws BindingException
+ */
+ public Object create(Collection<Object> collection) throws BindingException {
+ return create(collection.size(), collection.iterator());
+ }
+
+ /**
+ * Create new array instance with initial values possibly borrowed from an interator.
+ * <p>
+ * The implementation iterate the iterator before returning.
+ *
+ * @param length array length
+ * @param values value iterator
+ * @return new instance
+ */
+ public abstract Object create(int length, Iterator<Object> values)
+ throws BindingException;\r
+ \r
+ /**\r
+ * \r
+ * @param length\r
+ * @return array\r
+ * @throws BindingException\r
+ */\r
+ public Object create(int length) throws BindingException\r
+ {\r
+ try {\r
+ return create(length, new Iterator<Object>() {\r
+ @Override\r
+ public boolean hasNext() {\r
+ return true;\r
+ }\r
+ \r
+ @Override\r
+ public Object next() {\r
+ try {\r
+ return componentBinding.createDefault();\r
+ } catch (BindingException e) {\r
+ throw new RuntimeBindingException(e);\r
+ }\r
+ }\r
+ \r
+ @Override\r
+ public void remove() {\r
+ }});\r
+ } catch (RuntimeBindingException e) {\r
+ throw e.getCause();\r
+ }\r
+ }
+
+ /**
+ * Create Array with initial values possibly borrowed from an java.lang.Array
+ *
+ * @param array
+ * @return array of ArrayType
+ * @throws BindingException
+ */
+ public abstract Object create(Object[] array)
+ throws BindingException;
+
+ public Object createUnchecked(Object...array)
+ throws RuntimeBindingException {
+ try {
+ return create(array);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }\r
+\r
+ @Override\r
+ public void readFrom(Binding srcBinding, Object src, Object dst)\r
+ throws BindingException {\r
+ \r
+ try {\r
+ \r
+ // Src Binding\r
+ ArrayBinding sb = (ArrayBinding) srcBinding;\r
+ // Src Component Binding\r
+ Binding scb = sb.getComponentBinding();\r
+ \r
+ // Dst Component Binding\r
+ Binding dcb = getComponentBinding();\r
+ \r
+ int newLen = sb.size(src);\r
+ int oldLen = size(dst);\r
+ boolean cbImmutable = dcb.isImmutable();\r
+ Adapter cloner = cbImmutable ? Bindings.adapterFactory.getAdapter(scb, dcb, false, true) : null;\r
+ \r
+ // Set elements\r
+ for (int i=0; i<newLen; i++) {\r
+ if (cbImmutable) {\r
+ // Create new instance\r
+ Object sc = sb.get(src, i);\r
+ Object dc = cloner.adapt(sc);\r
+ if (i<oldLen) set(dst, i, dc); else add(dst, dc); \r
+ } else \r
+ if (i<oldLen) {\r
+ // Read over old instance\r
+ Object dc = get(dst, i);\r
+ Object sc = sb.get(src, i);\r
+ dc = dcb.readFromTry(scb, sc, dc);\r
+ // Safety-writeback - clone/reference nature of get is unknown\r
+ set(dst, i, dc);\r
+ } else {\r
+ // New instance\r
+ Object sc = sb.get(src, i);\r
+ Object dc = dcb.createDefault();\r
+ dc = dcb.readFromTry(scb, sc, dc);\r
+ add(dst, dc);\r
+ }\r
+ }\r
+ \r
+ // Remove excess elements\r
+ if (oldLen>newLen) {\r
+ setSize(dst, newLen);\r
+ }\r
+ \r
+ if (!Bindings.equals(srcBinding, src, this, dst))\r
+ throw new BindingException("internal error");\r
+\r
+ } catch (AdaptException e) {\r
+ throw new BindingException(e);\r
+ } catch (AdapterConstructionException e) {\r
+ throw new BindingException(e);\r
+ }\r
+ \r
+ } \r
+\r
+
+ public void add(Object array, Object element) throws BindingException
+ {
+ int size = size(array);
+ add(array, size, element);
+ }
+ public abstract void add(Object array, int index, Object element) throws BindingException, IndexOutOfBoundsException;
+ public void remove(Object array, int index) throws BindingException, IndexOutOfBoundsException
+ {
+ remove(array, index, 1);
+ }
+ public abstract void remove(Object array, int index, int count) throws BindingException, IndexOutOfBoundsException;
+ public abstract Object get(Object array, int index) throws BindingException, IndexOutOfBoundsException;
+ public abstract void getAll(Object array, Object[] result) throws BindingException;
+ public abstract void set(Object array, int index, Object value) throws BindingException;\r
+ public abstract void setSize(Object array, int newSize) throws BindingException;
+
+ public abstract int size(Object array) throws BindingException;
+
+ @Override
+ public void accept(Visitor1 v, Object obj) {
+ v.visit(this, obj);
+ }
+
+ @Override
+ public <T> T accept(Visitor<T> v) {
+ return v.visit(this);
+ }
+
+ /**
+ * Assert the instance is valid and follows restrictions set in data type.
+ * Assertions:
+ * 1. The Length of the array meets ArrayType range
+ * 2. Recursive assertion of each element
+ *
+ * @param obj the instance
+ * @throws BindingException on invalid instance
+ */
+ public void assertInstaceIsValid(Object obj, Set<Object> validInstances)
+ throws BindingException
+ {
+ ArrayType type = type();
+ // Assert The Length of the array meets ArrayType range
+ int length = size(obj);
+ Range range = type.getLength();
+ if (range!=null && !range.contains(length)) {
+ throw new BindingException("Array length (="+length+")out of bounds. "+range+" was expected.");
+ }
+ // Recursive assertion of each element
+ for (int i=0; i<length; i++)
+ {
+ Object component = get(obj, i);
+ componentBinding.assertInstaceIsValid(component, validInstances);
+ }
+ }
+
+ @Override
+ public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
+ int result = 1;
+ int len = size(value);
+ for (int i=0; i<len; i++) {
+ Object element = get(value, i);
+ result = 31*result + componentBinding.deepHashValue(element, hashedObjects);
+ }
+ return result;
+ }
+
+ @Override
+ public int deepCompare(Object o1, Object o2,
+ Set<IdentityPair<Object, Object>> compareHistory)
+ throws BindingException {
+ // Compare Lengths
+ int l1 = size(o1);
+ int l2 = size(o2);
+ int dif = l1 - l2;
+ if (dif!=0) return dif;
+ // Compare elements
+ Binding c = getComponentBinding();
+ for (int i=0; i<l1; i++) {
+ Object e1 = get(o1, i);
+ Object e2 = get(o2, i);
+ dif = c.deepCompare(e1, e2, compareHistory);
+ if (dif!=0) return dif;
+ }
+ return 0;
+ }
+\r
+ @Override\r
+ protected void toString(Object value, BindingPrintContext ctx) throws BindingException {\r
+ Binding componentBinding = getComponentBinding();\r
+ ctx.b.append('[');\r
+ int size = size(value);\r
+ boolean numberArray = componentBinding instanceof NumberBinding;\r
+ boolean hasMany = size > 1;\r
+ for(int i=0;i<size;++i) {\r
+ if(i>0) {\r
+ ctx.b.append(", ");\r
+ if ( hasMany && !numberArray && !ctx.singleLine ) {\r
+ ctx.b.append("\n");\r
+ }\r
+ }\r
+ componentBinding.toString(get(value, i), ctx);\r
+ }\r
+ ctx.b.append(']');\r
+ }\r
+ \r
+ /**\r
+ * Get the number of component bindings\r
+ */\r
+ @Override\r
+ public int getComponentCount() {\r
+ return 1;\r
+ }\r
+ \r
+ @Override\r
+ public Binding getComponentBinding(int index) {\r
+ if (index!=0) throw new IllegalArgumentException();\r
+ return componentBinding;\r
+ }\r
+\r
+ @Override\r
+ protected boolean deepEquals(Object obj,\r
+ Set<IdentityPair<Binding, Binding>> compareHistory) {\r
+ return super.deepEquals( obj, compareHistory ) && componentBinding.equals(((ArrayBinding)obj).componentBinding, compareHistory);\r
+ }\r
+ \r
+ @Override\r
+ public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
+ return super.deepHashCode(hashedObjects) + 13 * componentBinding.hashCode(hashedObjects);\r
+ }\r
+}