]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/RecordBinding.java
Migrated source code from Simantics SVN
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / RecordBinding.java
diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/RecordBinding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/RecordBinding.java
new file mode 100644 (file)
index 0000000..416abf8
--- /dev/null
@@ -0,0 +1,420 @@
+/*******************************************************************************\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
+}