X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2FBinding.java;h=22acacdc2ddcf8512b024a4bed3c75605fc3ed72;hb=95bce3521a3c97f463c3d533a36a606c7ae6f0aa;hp=6bc33f7b75199d86cdf5914560e9bc941466e3fe;hpb=969bd23cab98a79ca9101af33334000879fb60c5;p=simantics%2Fplatform.git diff --git a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java index 6bc33f7b7..22acacdc2 100644 --- a/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java +++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java @@ -1,88 +1,88 @@ -/******************************************************************************* - * 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 - *******************************************************************************/ +/******************************************************************************* + * 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; -import java.io.Reader; -import java.util.Comparator; -import java.util.HashSet; -import java.util.IdentityHashMap; -import java.util.Random; -import java.util.Set; - -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; +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; -/** - * This class represents connection between abstract datatype and java class. - * A binding allows an access to an Object in scope of a datatype.

- * - * 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).

- * - * 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 +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.

+ * + * 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).

+ * + * 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 * @author Hannu Niemisto */ public abstract class Binding implements Comparator { protected Datatype type; - protected transient Serializer binarySerializer; + protected transient Serializer binarySerializer; /** * Get Value Type @@ -132,49 +132,49 @@ public abstract class Binding implements Comparator { } public abstract T accept(Visitor v); - - /** - * 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 - * null 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; - } + + /** + * 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 + * null 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. * * Binary Serialization format * - * @return serializer for this binding + * @return serializer for this binding * @deprecated Instead use {@link Bindings#getSerializerUnchecked(Binding)} or {@link SerializerScheme#getSerializerUnchecked(Binding)} - */ + */ @Deprecated public Serializer serializer() throws RuntimeSerializerConstructionException - { - //return Bindings.serializationFactory.getSerializerUnchecked(this); + { + //return Bindings.serializationFactory.getSerializerUnchecked(this); if (binarySerializer==null) { synchronized (this) { - if (binarySerializer==null) binarySerializer = Bindings.serializationFactory.getSerializerUnchecked(this); - } + if (binarySerializer==null) binarySerializer = Bindings.serializationFactory.getSerializerUnchecked(this); + } } - return binarySerializer; + return binarySerializer; } public abstract boolean isInstance(Object obj); @@ -187,57 +187,57 @@ public abstract class Binding implements Comparator { */ public boolean isImmutable() { return false; - } - - /** - * Read values from one object to another. - * - * @param srcBinding - * @param src - * @param dst valid object of this binding - * @throws BindingException - */ + } + + /** + * 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; - - /** - * 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 ); - } - } + + /** + * 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 { public String printValueDefinition(Object value, boolean singleLine) throws IOException, BindingException { DataValueRepository valueRepository = new DataValueRepository(); - 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(); + 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 { * first argument is less than, equal to, or greater than the * second. * @throws BindingException if object cannot be handled by a binding - */ + */ @Override public int compare(Object o1, Object o2) throws RuntimeBindingException @@ -519,241 +519,241 @@ public abstract class Binding implements Comparator { return accept(new DefaultValue()); } - /** - * 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(); - } - } + /** + * 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 )); - } - - 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 null 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>()); - } - - /** - * 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> compareHistory) { - if (this == obj) return true; - if (this.getClass() != obj.getClass()) return false; - - IdentityPair pair = new IdentityPair(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>). - * Child classes should always make a call to super.deepEquals(). - */ - protected boolean deepEquals(Object obj, Set> 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()); - } - - /** - * 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 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), - * passing on the value provided to #deepHashCode. - */ - protected int deepHashCode(IdentityHashMap hashedObjects) { - return baseHashCode(); - } -} + } + + 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 null 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>()); + } + + /** + * 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> compareHistory) { + if (this == obj) return true; + if (this.getClass() != obj.getClass()) return false; + + IdentityPair pair = new IdentityPair(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>). + * Child classes should always make a call to super.deepEquals(). + */ + protected boolean deepEquals(Object obj, Set> 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()); + } + + /** + * 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 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), + * passing on the value provided to #deepHashCode. + */ + protected int deepHashCode(IdentityHashMap hashedObjects) { + return baseHashCode(); + } +}