]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java
Fixed all line endings of the repository
[simantics/platform.git] / bundles / org.simantics.databoard / src / org / simantics / databoard / binding / Binding.java
index 6bc33f7b75199d86cdf5914560e9bc941466e3fe..22acacdc2ddcf8512b024a4bed3c75605fc3ed72 100644 (file)
@@ -1,88 +1,88 @@
-/*******************************************************************************\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
+/*******************************************************************************
+ *  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.binding;
 
-import java.io.IOException;\r
-import java.io.Reader;\r
-import java.util.Comparator;\r
-import java.util.HashSet;\r
-import java.util.IdentityHashMap;\r
-import java.util.Random;\r
-import java.util.Set;\r
-\r
-import org.simantics.databoard.Bindings;\r
-import org.simantics.databoard.accessor.error.AccessorConstructionException;\r
-import org.simantics.databoard.accessor.error.AccessorException;\r
-import org.simantics.databoard.accessor.reference.ChildReference;\r
-import org.simantics.databoard.adapter.AdaptException;\r
-import org.simantics.databoard.adapter.AdapterConstructionException;\r
-import org.simantics.databoard.adapter.RuntimeAdaptException;\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.binding.mutable.MutableVariant;\r
-import org.simantics.databoard.binding.mutable.Variant;\r
-import org.simantics.databoard.binding.util.DefaultValue;\r
-import org.simantics.databoard.binding.util.RandomValue;\r
-import org.simantics.databoard.parser.DataParser;\r
-import org.simantics.databoard.parser.DataValuePrinter;\r
-import org.simantics.databoard.parser.ParseException;\r
-import org.simantics.databoard.parser.PrintFormat;\r
-import org.simantics.databoard.parser.repository.DataTypeSyntaxError;\r
-import org.simantics.databoard.parser.repository.DataValueRepository;\r
-import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;\r
-import org.simantics.databoard.serialization.Serializer;\r
-import org.simantics.databoard.serialization.SerializerFactory;\r
-import org.simantics.databoard.serialization.SerializerScheme;\r
-import org.simantics.databoard.type.Datatype;\r
-import org.simantics.databoard.util.IdentityPair;\r
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.IdentityHashMap;
+import java.util.Random;
+import java.util.Set;
 
-/**\r
- * This class represents connection between abstract datatype and java class.\r
- * A binding allows an access to an Object in scope of a datatype. <p>\r
- * \r
- * For example, IntegerBinding gives unified access to any integer class\r
- * (Integer, int, MutableInteger, UnsignedInteger). There is same unification\r
- * for primitive types and constructed types (record, map, array, union, variant). <p>\r
- * \r
- * You can get a hold of binding several ways:\r
- *   1) Use one of the default bindings e.g. {@link Bindings#BYTE_ARRAY}\r
- *   2) Create one using Datatype {@link Bindings#getMutableBinding(Datatype)}\r
- *   3) Create one using Reflectiong {@link Bindings#getBinding(Class)}\r
- *   4) Instantiate binding your self. e.g. new TreeMapBinding( Bindings.STRING, Bindings.STRING );\r
- *   5) Sub-class one of the abstract binding classes \r
- *             @see BooleanBinding\r
- *             @see ByteBinding\r
- *             @see IntegerBinding\r
- *             @see LongBinding\r
- *             @see FloatBinding\r
- *             @see DoubleBinding\r
- *             @see StringBinding\r
- *             @see RecordBinding\r
- *             @see ArrayBinding\r
- *             @see MapBinding\r
- *             @see OptionalBinding\r
- *             @see UnionBinding\r
- *             @see VariantBinding\r
- * \r
- * See examples/BindingExample.java \r
- * @see Bindings Facade class Bindings provices extra functionality. \r
- * @author Toni Kalajainen <toni.kalajainen@vtt.fi>\r
+import org.simantics.databoard.Bindings;
+import org.simantics.databoard.accessor.error.AccessorConstructionException;
+import org.simantics.databoard.accessor.error.AccessorException;
+import org.simantics.databoard.accessor.reference.ChildReference;
+import org.simantics.databoard.adapter.AdaptException;
+import org.simantics.databoard.adapter.AdapterConstructionException;
+import org.simantics.databoard.adapter.RuntimeAdaptException;
+import org.simantics.databoard.binding.error.BindingException;
+import org.simantics.databoard.binding.error.RuntimeBindingException;
+import org.simantics.databoard.binding.impl.BindingPrintContext;
+import org.simantics.databoard.binding.mutable.MutableVariant;
+import org.simantics.databoard.binding.mutable.Variant;
+import org.simantics.databoard.binding.util.DefaultValue;
+import org.simantics.databoard.binding.util.RandomValue;
+import org.simantics.databoard.parser.DataParser;
+import org.simantics.databoard.parser.DataValuePrinter;
+import org.simantics.databoard.parser.ParseException;
+import org.simantics.databoard.parser.PrintFormat;
+import org.simantics.databoard.parser.repository.DataTypeSyntaxError;
+import org.simantics.databoard.parser.repository.DataValueRepository;
+import org.simantics.databoard.serialization.RuntimeSerializerConstructionException;
+import org.simantics.databoard.serialization.Serializer;
+import org.simantics.databoard.serialization.SerializerFactory;
+import org.simantics.databoard.serialization.SerializerScheme;
+import org.simantics.databoard.type.Datatype;
+import org.simantics.databoard.util.IdentityPair;
+
+/**
+ * This class represents connection between abstract datatype and java class.
+ * A binding allows an access to an Object in scope of a datatype. <p>
+ * 
+ * For example, IntegerBinding gives unified access to any integer class
+ * (Integer, int, MutableInteger, UnsignedInteger). There is same unification
+ * for primitive types and constructed types (record, map, array, union, variant). <p>
+ * 
+ * You can get a hold of binding several ways:
+ *   1) Use one of the default bindings e.g. {@link Bindings#BYTE_ARRAY}
+ *   2) Create one using Datatype {@link Bindings#getMutableBinding(Datatype)}
+ *   3) Create one using Reflectiong {@link Bindings#getBinding(Class)}
+ *   4) Instantiate binding your self. e.g. new TreeMapBinding( Bindings.STRING, Bindings.STRING );
+ *   5) Sub-class one of the abstract binding classes 
+ *             @see BooleanBinding
+ *             @see ByteBinding
+ *             @see IntegerBinding
+ *             @see LongBinding
+ *             @see FloatBinding
+ *             @see DoubleBinding
+ *             @see StringBinding
+ *             @see RecordBinding
+ *             @see ArrayBinding
+ *             @see MapBinding
+ *             @see OptionalBinding
+ *             @see UnionBinding
+ *             @see VariantBinding
+ * 
+ * See examples/BindingExample.java 
+ * @see Bindings Facade class Bindings provices extra functionality. 
+ * @author Toni Kalajainen <toni.kalajainen@vtt.fi>
  * @author Hannu Niemisto
  */
 public abstract class Binding implements Comparator<Object> {
 
        protected Datatype type;
-       protected transient Serializer binarySerializer;\r
+       protected transient Serializer binarySerializer;
        
        /**
         * Get Value Type
@@ -132,49 +132,49 @@ public abstract class Binding implements Comparator<Object> {
     }
     
     public abstract <T> T accept(Visitor<T> v);
-    \r
-    /**\r
-     * Absolutely for databoard-internal use only. Used in caching serializers\r
-     * constructed by {@link SerializerFactory}.\r
-     * \r
-     * @return the serializer that has been cached in this Binding instance or\r
-     *         <code>null</code> if nothing is cached yet\r
-     * @since Simantics 1.15.1\r
-     */\r
-    public Serializer cachedSerializer() {\r
-        return binarySerializer;\r
-    }\r
-\r
-    /**\r
-     * Absolutely for databoard-internal use only. Used in caching serializers\r
-     * constructed by {@link SerializerFactory}.\r
-     * \r
-     * @param serializer the cached serializer to set for this binding\r
-     * @since Simantics 1.15.1\r
-     */\r
-    public void cacheSerializer(Serializer serializer) {\r
-        this.binarySerializer = serializer;\r
-    }\r
+    
+    /**
+     * Absolutely for databoard-internal use only. Used in caching serializers
+     * constructed by {@link SerializerFactory}.
+     * 
+     * @return the serializer that has been cached in this Binding instance or
+     *         <code>null</code> if nothing is cached yet
+     * @since Simantics 1.15.1
+     */
+    public Serializer cachedSerializer() {
+        return binarySerializer;
+    }
+
+    /**
+     * Absolutely for databoard-internal use only. Used in caching serializers
+     * constructed by {@link SerializerFactory}.
+     * 
+     * @param serializer the cached serializer to set for this binding
+     * @since Simantics 1.15.1
+     */
+    public void cacheSerializer(Serializer serializer) {
+        this.binarySerializer = serializer;
+    }
 
     /**
      * Get or create default serializer.
      * 
      * <a href="http://dev.simantics.org/index.php/Org.simantics.databoard_Manual#Binary_Serialization">Binary Serialization format</a>
      *  
-     * @return serializer for this binding\r
+     * @return serializer for this binding
      * @deprecated Instead use {@link Bindings#getSerializerUnchecked(Binding)} or {@link SerializerScheme#getSerializerUnchecked(Binding)} 
-     */\r
+     */
     @Deprecated 
     public Serializer serializer()
     throws RuntimeSerializerConstructionException
-    {          \r
-       //return Bindings.serializationFactory.getSerializerUnchecked(this);\r
+    {          
+       //return Bindings.serializationFactory.getSerializerUnchecked(this);
        if (binarySerializer==null) {  
                synchronized (this) {
-                       if (binarySerializer==null) binarySerializer = Bindings.serializationFactory.getSerializerUnchecked(this);\r
-               }\r
+                       if (binarySerializer==null) binarySerializer = Bindings.serializationFactory.getSerializerUnchecked(this);
+               }
        }
-       return binarySerializer;\r
+       return binarySerializer;
     }
     
     public abstract boolean isInstance(Object obj);    
@@ -187,57 +187,57 @@ public abstract class Binding implements Comparator<Object> {
      */
     public boolean isImmutable() {
        return false;
-    }\r
-    \r
-    /**\r
-     * Read values from one object to another.\r
-     * \r
-     * @param srcBinding\r
-     * @param src\r
-     * @param dst valid object of this binding\r
-     * @throws BindingException\r
-     */\r
+    }
+    
+    /**
+     * Read values from one object to another.
+     * 
+     * @param srcBinding
+     * @param src
+     * @param dst valid object of this binding
+     * @throws BindingException
+     */
     public abstract void readFrom(Binding srcBinding, Object src, Object dst) throws BindingException;
-    \r
-    /**\r
-     * Read values from another object.\r
-     * \r
-     * @param srcBinding\r
-     * @param src\r
-     * @param dst valid object of this binding\r
-     * @throws RuntimeBindingException\r
-     */\r
-    public void readFromUnchecked(Binding srcBinding, Object src, Object dst) throws RuntimeBindingException\r
-    {\r
-       try {\r
-                       readFrom(srcBinding, src, dst);\r
-               } catch (BindingException e) {\r
-                       throw new RuntimeBindingException( e );\r
-               }\r
-    }\r
-    \r
-    /**\r
-     * Read values from one object to another.\r
-     * \r
-     * @param srcBinding\r
-     * @param src\r
-     * @param dst valid object of this binding\r
-     * @return dst or new instance if could not be read to dst\r
-     * @throws BindingException\r
-     */\r
-    public Object readFromTry(Binding srcBinding, Object src, Object dst) throws BindingException\r
-    {\r
-       readFrom(srcBinding, src, dst);\r
-       return dst;\r
-    }\r
-    public Object readFromTryUnchecked(Binding srcBinding, Object src, Object dst) throws BindingException\r
-    {\r
-       try {\r
-               return readFromTry(srcBinding, src, dst);\r
-               } catch (BindingException e) {\r
-                       throw new RuntimeBindingException( e );\r
-               }\r
-    }\r
+    
+    /**
+     * Read values from another object.
+     * 
+     * @param srcBinding
+     * @param src
+     * @param dst valid object of this binding
+     * @throws RuntimeBindingException
+     */
+    public void readFromUnchecked(Binding srcBinding, Object src, Object dst) throws RuntimeBindingException
+    {
+       try {
+                       readFrom(srcBinding, src, dst);
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException( e );
+               }
+    }
+    
+    /**
+     * Read values from one object to another.
+     * 
+     * @param srcBinding
+     * @param src
+     * @param dst valid object of this binding
+     * @return dst or new instance if could not be read to dst
+     * @throws BindingException
+     */
+    public Object readFromTry(Binding srcBinding, Object src, Object dst) throws BindingException
+    {
+       readFrom(srcBinding, src, dst);
+       return dst;
+    }
+    public Object readFromTryUnchecked(Binding srcBinding, Object src, Object dst) throws BindingException
+    {
+       try {
+               return readFromTry(srcBinding, src, dst);
+               } catch (BindingException e) {
+                       throw new RuntimeBindingException( e );
+               }
+    }
     
     /**
      * Assert the obj is valid data type
@@ -332,12 +332,12 @@ public abstract class Binding implements Comparator<Object> {
     public String printValueDefinition(Object value, boolean singleLine) throws IOException, BindingException
     {
                DataValueRepository valueRepository = new DataValueRepository();
-               valueRepository.put("value", this, value);\r
-               StringBuilder sb = new StringBuilder();\r
-               DataValuePrinter vp = new DataValuePrinter(sb, valueRepository);\r
-               vp.setFormat( PrintFormat.MULTI_LINE );\r
-               vp.print(this, value);\r
-               return sb.toString();\r
+               valueRepository.put("value", this, value);
+               StringBuilder sb = new StringBuilder();
+               DataValuePrinter vp = new DataValuePrinter(sb, valueRepository);
+               vp.setFormat( PrintFormat.MULTI_LINE );
+               vp.print(this, value);
+               return sb.toString();
     }
     
     /**
@@ -422,7 +422,7 @@ public abstract class Binding implements Comparator<Object> {
      *                first argument is less than, equal to, or greater than the
      *        second.
      * @throws BindingException if object cannot be handled by a binding
-     */    \r
+     */    
     @Override
     public int compare(Object o1, Object o2)
     throws RuntimeBindingException
@@ -519,241 +519,241 @@ public abstract class Binding implements Comparator<Object> {
                return accept(new DefaultValue());
     }
     
-    /**\r
-     * Create random valid value.\r
-     * \r
-     * @param seed random seed\r
-     * @return random value\r
-     * @throws BindingException\r
-     */\r
-    public Object createRandom(int seed) \r
-    throws BindingException\r
-    {\r
-       try {\r
-               return accept(new RandomValue( seed ));\r
-       } catch (RuntimeBindingException e) {\r
-               throw e.getCause();\r
-       }\r
-    }\r
-    \r
-    /**\r
-     * Create random valid value.\r
-     * \r
-     * @param rv random seed\r
-     * @return random value\r
-     * @throws BindingException\r
-     */\r
-    public Object createRandom(RandomValue rv) \r
-    throws BindingException\r
-    {\r
-       try {\r
-               return accept(rv);\r
-       } catch (RuntimeBindingException e) {\r
-               throw e.getCause();\r
-       }\r
-    }\r
-    \r
-    /**\r
-     * Create random valid value.\r
-     * \r
-     * @param random random seed\r
-     * @return random value\r
-     * @throws BindingException\r
-     */\r
-    public Object createRandom(Random random) \r
-    throws BindingException\r
-    {\r
-       try {\r
-               return accept(new RandomValue( random ));\r
-       } catch (RuntimeBindingException e) {\r
-               throw e.getCause();\r
-       }\r
-    }\r
+    /**
+     * Create random valid value.
+     * 
+     * @param seed random seed
+     * @return random value
+     * @throws BindingException
+     */
+    public Object createRandom(int seed) 
+    throws BindingException
+    {
+       try {
+               return accept(new RandomValue( seed ));
+       } catch (RuntimeBindingException e) {
+               throw e.getCause();
+       }
+    }
+    
+    /**
+     * Create random valid value.
+     * 
+     * @param rv random seed
+     * @return random value
+     * @throws BindingException
+     */
+    public Object createRandom(RandomValue rv) 
+    throws BindingException
+    {
+       try {
+               return accept(rv);
+       } catch (RuntimeBindingException e) {
+               throw e.getCause();
+       }
+    }
+    
+    /**
+     * Create random valid value.
+     * 
+     * @param random random seed
+     * @return random value
+     * @throws BindingException
+     */
+    public Object createRandom(Random random) 
+    throws BindingException
+    {
+       try {
+               return accept(new RandomValue( random ));
+       } catch (RuntimeBindingException e) {
+               throw e.getCause();
+       }
+    }
     
  
     public Object createRandomUnchecked(int seed)
     throws RuntimeBindingException
     {
                return accept(new RandomValue( seed ));
-    }\r
-    \r
-    public String toString(Object value) throws BindingException {\r
-       BindingPrintContext ctx = new BindingPrintContext();\r
-       toString(value, ctx);\r
-       return ctx.b.toString();\r
-    }\r
-\r
-    public String toStringUnchecked(Object value) {\r
-       try {\r
-               BindingPrintContext ctx = new BindingPrintContext();\r
-               toString(value, ctx);\r
-               return ctx.b.toString();\r
-       } catch ( BindingException e ) {\r
-               return e.toString();\r
-       }\r
-    }\r
-    \r
-    public String toString(Object value, boolean singleLine) throws BindingException {\r
-       BindingPrintContext ctx = new BindingPrintContext();\r
-       ctx.singleLine = singleLine;\r
-       toString(value, ctx);\r
-       return ctx.b.toString();\r
-    }\r
-\r
-       protected abstract void toString(Object value, BindingPrintContext ctx) throws BindingException;\r
-       \r
-       \r
-       /** \r
-        * Get component binding count\r
-        * \r
-        * @return component count\r
-        */\r
-       public abstract int getComponentCount();\r
-       \r
-       /**\r
-        * Get a component value of a structured data object.\r
-        * @param object The structured data object\r
-        * @param ref The child component reference\r
-        * @return The value of the data component\r
-        * @throws BindingException\r
-        */\r
-       public Object getComponent(Object object, ChildReference ref) throws BindingException {\r
-           Variant value = new MutableVariant(this, object);\r
-           try {\r
-            return value.getComponent(ref).getValue();\r
-        } catch ( AccessorConstructionException e ) {\r
-            throw new BindingException("Component access failed.", e);\r
-        }\r
-       }\r
-       \r
-       /**\r
-        * Get a component value of a structured data object.\r
-     * @param object The structured data object\r
-     * @param ref The child component reference\r
-        * @param binding The output data binding for the component value\r
-     * @return The value of the data component\r
-        * @throws BindingException\r
-        */\r
-       public Object getComponent(Object object, ChildReference ref, Binding binding) throws BindingException {\r
-           Binding componentBinding = getComponentBinding( ref );\r
-           Variant value = new MutableVariant(this, object);\r
-           try {\r
-            return Bindings.adapt( value.getComponent( ref ), componentBinding, binding );\r
-        } catch ( AdaptException | AccessorConstructionException e ) {\r
-            throw new BindingException("Component access failed.", e);\r
-        }\r
-       }\r
-       \r
-       /**\r
-        * Set the value of a component in a structured data object.\r
-     * @param object The structured data object\r
-     * @param ref The child component reference\r
-        * @param binding Data type binding for the component value\r
-        * @param componentValue The new child component value\r
-        * @throws BindingException\r
-        */\r
-    public void setComponent( Object object, ChildReference ref, Binding binding, Object componentValue ) throws BindingException {\r
-        MutableVariant value = new MutableVariant( this, object );\r
-        try {\r
-            value.setComponent( ref, binding, componentValue );\r
-        } catch ( AccessorException | AccessorConstructionException e ) {\r
-            throw new BindingException("Component access failed.", e );\r
-        }\r
-    }\r
-    \r
-       /**\r
-        * Get component binding\r
-        * @param index\r
-        * @return binding\r
-        */\r
-    public abstract Binding getComponentBinding(int index);\r
-    \r
-    /**\r
-     * Get component binding\r
-     * @param path child path or <tt>null</tt> to return this.\r
-     * @return binding \r
-     * @throws IllegalArgumentException if path cannot be applied to this binding\r
-     */\r
-    public abstract Binding getComponentBinding(ChildReference path);\r
-       \r
-    @Override\r
-    /**\r
-     * Each child class implements #deepEquals or #baseEquals or neither, depending on\r
-     * whether it includes references to child Binding instances or other fields.\r
-     * @see java.lang.Object#equals(java.lang.Object)\r
-     */\r
-    final public boolean equals(Object obj) {\r
-       if (this == obj) return true;\r
-       if (obj == null) return false;\r
-       if (this.getClass() != obj.getClass()) return false;\r
-       \r
-       return equals(obj, new HashSet<IdentityPair<Binding, Binding>>());\r
-    }\r
-    \r
-    /**\r
-     * Perform a deep equality check between this Binding object and another,\r
-     * with a memory for recursive references. Child classes should implement either the\r
-     * #deepEquals or #baseEquals method, or neither if there is no new data to compare.\r
-     */\r
-    final protected boolean equals(Object obj, Set<IdentityPair<Binding, Binding>> compareHistory) {\r
-        if (this == obj) return true;\r
-        if (this.getClass() != obj.getClass()) return false;\r
-        \r
-        IdentityPair<Binding, Binding> pair = new IdentityPair<Binding, Binding>(this, (Binding)obj); \r
-        if (compareHistory.contains(pair)) return true;\r
-        \r
-        compareHistory.add(pair);\r
-        return deepEquals(obj, compareHistory);\r
-    }\r
-    \r
-    /**\r
-     * Perform a comparison of the fields of this Binding instance. Always make a call to super.baseEquals().\r
-     */\r
-    protected boolean baseEquals(Object obj) {\r
-       return type == null ? ((Binding)obj).type == null : type.equals(((Binding)obj).type);\r
-    }\r
-    \r
-    /**\r
-     * Perform a deep comparison of this Binding object with another.\r
-     * Matching child Binding instances must be compared with #equals(Object, Set<IdentityPair<Binding, Binding>>).\r
-     * Child classes should always make a call to super.deepEquals().\r
-     */\r
-    protected boolean deepEquals(Object obj, Set<IdentityPair<Binding, Binding>> compareHistory) {\r
-       return baseEquals(obj);\r
-    }\r
-    \r
-    @Override\r
-    /**\r
-     * Each child class implements #deepHashCode, #baseHashCode or neither, depending on whether it\r
-     * includes child Binding references or other fields.\r
-     */\r
-    final public int hashCode() {\r
-       return deepHashCode(new IdentityHashMap<Object,Object>());\r
-    }\r
-    \r
-    /**\r
-     * Calculate a deep hash code for this Binding instance.\r
-     * Child classes should implement either deepHashCode or baseHashCode, or neither, if there is no new data.\r
-     */\r
-    final protected int hashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
-        if (hashedObjects.containsKey(this)) return 0;\r
-        hashedObjects.put(this, null);\r
-        return deepHashCode(hashedObjects);\r
-    }\r
-    \r
-    /**\r
-     * Calculate a hash code based on the fields of this Binding instance. Child classes must always make a call to super.baseHashCode(). \r
-     */\r
-    protected int baseHashCode() {\r
-       return getClass().hashCode() + (type != null ? 3 * type.hashCode() : 0);\r
-    }\r
-    \r
-    /**\r
-     * Perform deep hash code calculation for this Binding instance.\r
-     * Child instance hash codes must be calculated with #hashCode(IdentityHashMap<Object, Object>),\r
-     * passing on the value provided to #deepHashCode.\r
-     */\r
-    protected int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {\r
-       return baseHashCode();\r
-    }\r
-}\r
+    }
+    
+    public String toString(Object value) throws BindingException {
+       BindingPrintContext ctx = new BindingPrintContext();
+       toString(value, ctx);
+       return ctx.b.toString();
+    }
+
+    public String toStringUnchecked(Object value) {
+       try {
+               BindingPrintContext ctx = new BindingPrintContext();
+               toString(value, ctx);
+               return ctx.b.toString();
+       } catch ( BindingException e ) {
+               return e.toString();
+       }
+    }
+    
+    public String toString(Object value, boolean singleLine) throws BindingException {
+       BindingPrintContext ctx = new BindingPrintContext();
+       ctx.singleLine = singleLine;
+       toString(value, ctx);
+       return ctx.b.toString();
+    }
+
+       protected abstract void toString(Object value, BindingPrintContext ctx) throws BindingException;
+       
+       
+       /** 
+        * Get component binding count
+        * 
+        * @return component count
+        */
+       public abstract int getComponentCount();
+       
+       /**
+        * Get a component value of a structured data object.
+        * @param object The structured data object
+        * @param ref The child component reference
+        * @return The value of the data component
+        * @throws BindingException
+        */
+       public Object getComponent(Object object, ChildReference ref) throws BindingException {
+           Variant value = new MutableVariant(this, object);
+           try {
+            return value.getComponent(ref).getValue();
+        } catch ( AccessorConstructionException e ) {
+            throw new BindingException("Component access failed.", e);
+        }
+       }
+       
+       /**
+        * Get a component value of a structured data object.
+     * @param object The structured data object
+     * @param ref The child component reference
+        * @param binding The output data binding for the component value
+     * @return The value of the data component
+        * @throws BindingException
+        */
+       public Object getComponent(Object object, ChildReference ref, Binding binding) throws BindingException {
+           Binding componentBinding = getComponentBinding( ref );
+           Variant value = new MutableVariant(this, object);
+           try {
+            return Bindings.adapt( value.getComponent( ref ), componentBinding, binding );
+        } catch ( AdaptException | AccessorConstructionException e ) {
+            throw new BindingException("Component access failed.", e);
+        }
+       }
+       
+       /**
+        * Set the value of a component in a structured data object.
+     * @param object The structured data object
+     * @param ref The child component reference
+        * @param binding Data type binding for the component value
+        * @param componentValue The new child component value
+        * @throws BindingException
+        */
+    public void setComponent( Object object, ChildReference ref, Binding binding, Object componentValue ) throws BindingException {
+        MutableVariant value = new MutableVariant( this, object );
+        try {
+            value.setComponent( ref, binding, componentValue );
+        } catch ( AccessorException | AccessorConstructionException e ) {
+            throw new BindingException("Component access failed.", e );
+        }
+    }
+    
+       /**
+        * Get component binding
+        * @param index
+        * @return binding
+        */
+    public abstract Binding getComponentBinding(int index);
+    
+    /**
+     * Get component binding
+     * @param path child path or <tt>null</tt> to return this.
+     * @return binding 
+     * @throws IllegalArgumentException if path cannot be applied to this binding
+     */
+    public abstract Binding getComponentBinding(ChildReference path);
+       
+    @Override
+    /**
+     * Each child class implements #deepEquals or #baseEquals or neither, depending on
+     * whether it includes references to child Binding instances or other fields.
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    final public boolean equals(Object obj) {
+       if (this == obj) return true;
+       if (obj == null) return false;
+       if (this.getClass() != obj.getClass()) return false;
+       
+       return equals(obj, new HashSet<IdentityPair<Binding, Binding>>());
+    }
+    
+    /**
+     * Perform a deep equality check between this Binding object and another,
+     * with a memory for recursive references. Child classes should implement either the
+     * #deepEquals or #baseEquals method, or neither if there is no new data to compare.
+     */
+    final protected boolean equals(Object obj, Set<IdentityPair<Binding, Binding>> compareHistory) {
+        if (this == obj) return true;
+        if (this.getClass() != obj.getClass()) return false;
+        
+        IdentityPair<Binding, Binding> pair = new IdentityPair<Binding, Binding>(this, (Binding)obj); 
+        if (compareHistory.contains(pair)) return true;
+        
+        compareHistory.add(pair);
+        return deepEquals(obj, compareHistory);
+    }
+    
+    /**
+     * Perform a comparison of the fields of this Binding instance. Always make a call to super.baseEquals().
+     */
+    protected boolean baseEquals(Object obj) {
+       return type == null ? ((Binding)obj).type == null : type.equals(((Binding)obj).type);
+    }
+    
+    /**
+     * Perform a deep comparison of this Binding object with another.
+     * Matching child Binding instances must be compared with #equals(Object, Set<IdentityPair<Binding, Binding>>).
+     * Child classes should always make a call to super.deepEquals().
+     */
+    protected boolean deepEquals(Object obj, Set<IdentityPair<Binding, Binding>> compareHistory) {
+       return baseEquals(obj);
+    }
+    
+    @Override
+    /**
+     * Each child class implements #deepHashCode, #baseHashCode or neither, depending on whether it
+     * includes child Binding references or other fields.
+     */
+    final public int hashCode() {
+       return deepHashCode(new IdentityHashMap<Object,Object>());
+    }
+    
+    /**
+     * Calculate a deep hash code for this Binding instance.
+     * Child classes should implement either deepHashCode or baseHashCode, or neither, if there is no new data.
+     */
+    final protected int hashCode(IdentityHashMap<Object, Object> hashedObjects) {
+        if (hashedObjects.containsKey(this)) return 0;
+        hashedObjects.put(this, null);
+        return deepHashCode(hashedObjects);
+    }
+    
+    /**
+     * Calculate a hash code based on the fields of this Binding instance. Child classes must always make a call to super.baseHashCode(). 
+     */
+    protected int baseHashCode() {
+       return getClass().hashCode() + (type != null ? 3 * type.hashCode() : 0);
+    }
+    
+    /**
+     * Perform deep hash code calculation for this Binding instance.
+     * Child instance hash codes must be calculated with #hashCode(IdentityHashMap<Object, Object>),
+     * passing on the value provided to #deepHashCode.
+     */
+    protected int deepHashCode(IdentityHashMap<Object, Object> hashedObjects) {
+       return baseHashCode();
+    }
+}