--- /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.HashSet;\r
+import java.util.IdentityHashMap;\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.accessor.reference.NameReference;\r
+import org.simantics.databoard.adapter.AdaptException;\r
+import org.simantics.databoard.binding.error.BindingException;\r
+import org.simantics.databoard.binding.error.RuntimeBindingException;\r
+import org.simantics.databoard.binding.impl.BindingPrintContext;\r
+import org.simantics.databoard.type.Component;\r
+import org.simantics.databoard.type.RecordType;\r
+import org.simantics.databoard.util.IdentityHashSet;\r
+import org.simantics.databoard.util.IdentityPair;\r
+
+
+/**
+ * This is a binding of a Record Type and a Java Object.
+ *
+ * @see RecordType
+ * @author Hannu Niemisto
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
+ */
+public abstract class RecordBinding extends Binding {
+
+ public Binding[] componentBindings;
+ \r
+ /**\r
+ * Get binding by field name\r
+ * @param fieldName\r
+ * @return binding or <code>null</code>\r
+ */\r
+ public Binding getComponentBinding(String fieldName) {\r
+ int fieldIndex = type().getComponentIndex2(fieldName);\r
+ if (fieldIndex<0) return null;\r
+ return getComponentBinding(fieldIndex);\r
+ }\r
+ \r
+ public Object getComponentObject(Object obj, String fieldName) throws BindingException {\r
+ int fieldIndex = type().getComponentIndex2(fieldName);\r
+ if (fieldIndex<0) return null;\r
+ return getComponent(obj, fieldIndex);\r
+ }\r
+ \r
+ public int getComponentIndex(String fieldName) {\r
+ return type().getComponentIndex2(fieldName);\r
+ }\r
+
+ public Binding getComponentBinding(int fieldIndex) {
+ return componentBindings[fieldIndex];
+ }
+
+ public Binding[] getComponentBindings() {
+ return componentBindings;
+ }
+
+ protected void setComponentBindings(Binding[] componentBindings) {
+ this.componentBindings = componentBindings;
+ }
+
+ @Override
+ public RecordType type() {
+ return (RecordType) type;
+ }
+
+ public int getComponentCount() {
+ return type().getComponentCount();
+ }
+
+ public abstract Object getComponent(Object obj, int index)
+ throws BindingException;
+
+ /**
+ * Create a record using values.
+ *
+ * Note! values may be consumed (used in the result)
+ *
+ * @param values
+ * @return new record
+ * @throws BindingException
+ */
+ public abstract Object create(Object ... values)
+ throws BindingException;
+
+ /**
+ * Creates partial and most likely invalid instance.
+ * This is used in two-phase construction of recursive instances.
+ *
+ * @return instance.
+ */
+ public abstract Object createPartial() throws BindingException;
+
+ public abstract void setComponents(Object obj, Object ... value) throws BindingException;
+ public abstract void setComponent(Object obj, int index, Object value) throws BindingException;
+\r
+ public void setComponent(Object obj, String fieldName, Object value) throws BindingException\r
+ {\r
+ int fieldIndex = type().getComponentIndex2(fieldName);\r
+ if ( fieldIndex<0 ) throw new BindingException("Field "+fieldName+" does not exist.");\r
+ setComponent(obj, fieldIndex, value);\r
+ }\r
+ \r
+ @Override\r
+ public void readFrom(Binding srcBinding, Object src, Object dst)\r
+ throws BindingException {\r
+ RecordBinding sb = (RecordBinding) srcBinding;\r
+ int len = getComponentCount();\r
+ if (sb.getComponentCount()!=len) throw new BindingException("field count mismatch");\r
+ try {\r
+ for (int i=0; i<len; i++) {\r
+ Binding dcb = getComponentBinding(i);\r
+ Binding scb = sb.getComponentBinding(i);\r
+ Object sc = sb.getComponent(src, i);\r
+ if (dcb.isImmutable()) {\r
+ Object cv = Bindings.clone(sc, scb, dcb);\r
+ setComponent(dst, i, cv);\r
+ } else {\r
+ Object v = getComponent(dst, i);\r
+ v = dcb.readFromTry(scb, sc, v);\r
+ setComponent(dst, i, v);\r
+ }\r
+ } \r
+ } catch (AdaptException e) {\r
+ throw new BindingException(e);\r
+ }\r
+ }\r
+
+ @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 obj is valid Record Type
+ *
+ * This asserts all fields are valid.
+ *
+ * @throws BindingException
+ */
+ @Override
+ public void assertInstaceIsValid(Object obj, Set<Object> validInstances) throws BindingException {
+ if (!isInstance(obj))
+ throw new BindingException("The object ("+obj+") is not correct instance.");
+ RecordType type = type();
+ int length = type.getComponentCount();
+
+ if (type.isReferable()) {
+ if (validInstances==null) {
+ validInstances = new IdentityHashSet<Object>();
+ }
+ if (validInstances.contains(obj)) return;
+ validInstances.add(obj);
+ }
+
+ for (int i=0; i<length; i++)
+ {
+ Binding componentBinding = getComponentBindings()[i];
+ Object componentValue = getComponent(obj, i);
+ componentBinding.assertInstaceIsValid(componentValue, validInstances);
+ }
+ }
+
+ @Override
+ public int deepHashValue(Object value, IdentityHashMap<Object, Object> hashedObjects) throws BindingException {
+ if (type().isReferable()) {
+ if (hashedObjects==null) {
+ hashedObjects = new IdentityHashMap<Object, Object>(1);
+ }
+ if (hashedObjects.containsKey(value)) {
+ return 0;
+ } else {
+ hashedObjects.put(value, null);
+ }
+ }
+
+ int result = 3;
+ int len = componentBindings.length;
+ for (int i=0; i<len; i++) {
+ Object element = getComponent(value, i);
+ result += 31*result + componentBindings[i].deepHashValue(element, hashedObjects);
+ }
+ return result;
+ }
+
+ @Override
+ public int deepCompare(Object o1, Object o2,
+ Set<IdentityPair<Object, Object>> compareHistory)
+ throws BindingException {
+ RecordType rt = (RecordType) type();
+
+ // Compare History is used to prevent infinite loops
+ if (rt.isReferable()) {
+ if (compareHistory==null) {
+ compareHistory = new HashSet<IdentityPair<Object, Object>>();
+ }
+ IdentityPair<Object, Object> p = new IdentityPair<Object, Object>(o1, o2);
+ if (compareHistory.contains(p)) return 0;
+ compareHistory.add(p);
+ }
+
+ int len = rt.getComponentCount();
+ for (int i=0; i<len; i++) {
+ Binding c = getComponentBindings()[i];
+ Object v1 = getComponent(o1, i);
+ Object v2 = getComponent(o2, i);
+ int dif = c.deepCompare(v1, v2, compareHistory);
+ if (dif!=0)\r
+ return dif;
+ }
+ return 0;
+ }
+
+ public Object createUnchecked(Object ... values) throws RuntimeBindingException
+ {
+ try {
+ return create(values);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ public void setComponentsUnchecked(Object obj, Object ... value) throws RuntimeBindingException {
+ try {
+ setComponents(obj, value);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ public void setComponentUnchecked(Object obj, int index, Object value) throws RuntimeBindingException {
+ try {
+ setComponent(obj, index, value);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+\r
+ protected void toStringAux(Object value, BindingPrintContext ctx) throws BindingException {\r
+ ctx.b.append('{');\r
+ Component[] components = type().getComponents();\r
+ boolean first = true;\r
+ for(int i=0;i<components.length;++i) {\r
+ if(first)\r
+ first = false;\r
+ else {\r
+ ctx.b.append(", ");\r
+ if (!ctx.singleLine) ctx.b.append('\n');\r
+ }\r
+ Binding binding = getComponentBinding(i);\r
+ Object cval = getComponent(value, i);\r
+ if(binding instanceof OptionalBinding && \r
+ !((OptionalBinding)binding).hasValue(cval))\r
+ continue;\r
+ Component component = components[i]; \r
+ ctx.b.append(component.name);\r
+ ctx.b.append(" = ");\r
+ binding.toString(cval, ctx);\r
+ }\r
+ ctx.b.append('}');\r
+ }\r
+ \r
+ @Override\r
+ protected void toString(Object value, BindingPrintContext ctx) throws BindingException {\r
+ if(type().isReferable()) {\r
+ if(ctx.refs.contains(value)) {\r
+ int val = ctx.refs.get(value);\r
+ if(val < 0) {\r
+ val = ctx.nameCount.value++;\r
+ ctx.refs.put(value, val);\r
+ }\r
+ ctx.b.append((char)('a' + val));\r
+ }\r
+ else {\r
+ ctx.refs.put(value, -1);\r
+ toStringAux(value, ctx);\r
+ int val = ctx.refs.remove(value);\r
+ if(val >= 0) {\r
+ ctx.b.append("/");\r
+ ctx.b.append((char)('a' + val));\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ toStringAux(value, ctx);\r
+ }\r
+ }
+\r
+ @Override\r
+ public Binding getComponentBinding(ChildReference path) throws IllegalArgumentException {\r
+ if (path==null) return this;\r
+ if (path instanceof IndexReference) {\r
+ IndexReference ir = (IndexReference) path;\r
+ return componentBindings[ir.index].getComponentBinding(path.childReference);\r
+ }\r
+ if (path instanceof NameReference) {\r
+ NameReference nr = (NameReference) path;\r
+ return getComponentBinding( nr.name ).getComponentBinding(path.childReference);\r
+ }\r
+ if (path instanceof LabelReference) {\r
+ LabelReference lr = (LabelReference) path; \r
+ try {\r
+ Integer i = new Integer(lr.label);\r
+ return getComponentBinding( i ).getComponentBinding(path.childReference);\r
+ } catch (NumberFormatException nfe) {\r
+ return getComponentBinding( lr.label ).getComponentBinding(path.childReference);\r
+ }\r
+ }\r
+ throw new IllegalArgumentException();\r
+ }\r
+ \r
+ @Override\r
+ public boolean isImmutable() {\r
+ return getComponentCount() == 0;\r
+ }\r
+ \r
+ public void setBoolean(Object r, int index, boolean z) throws BindingException\r
+ {\r
+ setComponent(r, index, ((BooleanBinding)componentBindings[index]).create(z));\r
+ }\r
+ \r
+ public boolean getBoolean(Object r, int index) throws BindingException\r
+ {\r
+ return ((BooleanBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+ \r
+ public void setByte(Object r, int index, byte x) throws BindingException\r
+ {\r
+ setComponent(r, index, ((ByteBinding)componentBindings[index]).create(x));\r
+ }\r
+ \r
+ public byte getByte(Object r, int index) throws BindingException\r
+ {\r
+ return ((ByteBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+\r
+ public void setInt(Object r, int index, int x) throws BindingException\r
+ {\r
+ setComponent(r, index, ((IntegerBinding)componentBindings[index]).create(x));\r
+ }\r
+ \r
+ public int getInt(Object r, int index) throws BindingException\r
+ {\r
+ return ((IntegerBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+ \r
+ public void setLong(Object r, int index, long x) throws BindingException\r
+ {\r
+ setComponent(r, index, ((LongBinding)componentBindings[index]).create(x));\r
+ }\r
+ \r
+ public long getLong(Object r, int index) throws BindingException\r
+ {\r
+ return ((LongBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+ \r
+ public void setFloat(Object r, int index, float x) throws BindingException\r
+ {\r
+ setComponent(r, index, ((FloatBinding)componentBindings[index]).create(x));\r
+ }\r
+ \r
+ public float getFloat(Object r, int index) throws BindingException\r
+ {\r
+ return ((FloatBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+ \r
+ public void setDouble(Object r, int index, double x) throws BindingException\r
+ {\r
+ setComponent(r, index, ((DoubleBinding)componentBindings[index]).create(x));\r
+ }\r
+ \r
+ public double getDouble(Object r, int index) throws BindingException\r
+ {\r
+ return ((DoubleBinding)componentBindings[index]).getValue_( getComponent(r, index) );\r
+ }\r
+\r
+ @Override\r
+ protected boolean deepEquals(Object obj,\r
+ Set<IdentityPair<Binding, Binding>> compareHistory) {\r
+ RecordBinding o = (RecordBinding)obj;\r
+ if (!super.deepEquals( obj, compareHistory ))\r
+ return false;\r
+ \r
+ if (componentBindings.length != o.componentBindings.length) return false;\r
+ \r
+ for (int i = 0; i < componentBindings.length; i++)\r
+ if (!componentBindings[i].equals(o.componentBindings[i], compareHistory))\r
+ return false;\r
+ \r
+ return true;\r
+ }\r
+\r
+ @Override\r
+ public int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
+ int code = super.deepHashCode(hashedObjects);\r
+ for (int i = 0; i < componentBindings.length; i++)\r
+ code = 17 * code + componentBindings[i].hashCode(hashedObjects);\r
+ return code;\r
+ }\r
+}