X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=blobdiff_plain;f=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2FBinding.java;fp=bundles%2Forg.simantics.databoard%2Fsrc%2Forg%2Fsimantics%2Fdataboard%2Fbinding%2FBinding.java;h=6bc33f7b75199d86cdf5914560e9bc941466e3fe;hb=969bd23cab98a79ca9101af33334000879fb60c5;hp=0000000000000000000000000000000000000000;hpb=866dba5cd5a3929bbeae85991796acb212338a08;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
new file mode 100644
index 000000000..6bc33f7b7
--- /dev/null
+++ b/bundles/org.simantics.databoard/src/org/simantics/databoard/binding/Binding.java
@@ -0,0 +1,759 @@
+/*******************************************************************************
+ * 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;
+
+/**
+ * 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;
+
+ /**
+ * Get Value Type
+ *
+ * @return value type
+ */
+ public Datatype type() {
+ return type;
+ }
+
+ protected void setType(Datatype type) {
+ this.type = type;
+ }
+
+ public interface Visitor1 {
+ void visit(ArrayBinding b, Object obj);
+ void visit(BooleanBinding b, Object obj);
+ void visit(DoubleBinding b, Object obj);
+ void visit(FloatBinding b, Object obj);
+ void visit(IntegerBinding b, Object obj);
+ void visit(ByteBinding b, Object obj);
+ void visit(LongBinding b, Object obj);
+ void visit(OptionalBinding b, Object obj);
+ void visit(RecordBinding b, Object obj);
+ void visit(StringBinding b, Object obj);
+ void visit(UnionBinding b, Object obj);
+ void visit(VariantBinding b, Object obj);
+ void visit(MapBinding b, Object obj);
+ }
+
+ public abstract void accept(Visitor1 v, Object obj);
+
+ public interface Visitor {
+ T visit(ArrayBinding b);
+ T visit(BooleanBinding b);
+ T visit(DoubleBinding b);
+ T visit(FloatBinding b);
+ T visit(IntegerBinding b);
+ T visit(ByteBinding b);
+ T visit(LongBinding b);
+ T visit(OptionalBinding b);
+ T visit(RecordBinding b);
+ T visit(StringBinding b);
+ T visit(UnionBinding b);
+ T visit(VariantBinding b);
+ T visit(MapBinding b);
+ }
+
+ 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;
+ }
+
+ /**
+ * Get or create default serializer.
+ *
+ * Binary Serialization format
+ *
+ * @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);
+ if (binarySerializer==null) {
+ synchronized (this) {
+ if (binarySerializer==null) binarySerializer = Bindings.serializationFactory.getSerializerUnchecked(this);
+ }
+ }
+ return binarySerializer;
+ }
+
+ public abstract boolean isInstance(Object obj);
+
+ /**
+ * Return true if the value is immutable.
+ * This question excludes the immutability of the component types.
+ *
+ * @return true
value if immutable
+ */
+ 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
+ */
+ 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 );
+ }
+ }
+
+ /**
+ * Assert the obj is valid data type
+ *
+ * @param obj the instance
+ * @throws BindingException on invalid instance
+ */
+ public void assertInstaceIsValid(Object obj)
+ throws BindingException
+ {
+ assertInstaceIsValid(obj, null);
+ }
+
+ /**
+ * Assert the obj is valid data type
+ *
+ * @param obj the instance
+ * @param validInstances optional set of already validated instances
+ * @throws BindingException on invalid instance
+ */
+ public abstract void assertInstaceIsValid(Object obj, Set validInstances)
+ throws BindingException;
+
+ /**
+ * Parse data value from a text to a value instance.
+ *
+ * Datavalue notation
+ *
+ * @param stream
+ * @return the value
+ * @throws BindingException
+ * @throws ParseException
+ */
+ public Object parseValue(Reader stream, DataValueRepository repository) throws DataTypeSyntaxError, BindingException
+ {
+ DataParser parser = new DataParser(stream);
+ try {
+ return new DataValueRepository().translate(parser.value(), this);
+ } catch(ParseException e) {
+ throw new DataTypeSyntaxError(e);
+ }
+ }
+
+ /**
+ * Parse data value from a text to a value instance.
+ *
+ * Datavalue Notation
+ *
+ * @param text
+ * @return the value
+ * @throws BindingException
+ * @throws ParseException
+ */
+ public Object parseValue(String text, DataValueRepository repository) throws DataTypeSyntaxError, BindingException
+ {
+ return repository.translate(text, this);
+ }
+
+ /**
+ * Parse data value from a text to a value instance.
+ *
+ * Datavalue Notation
+ *
+ * @param text
+ * @return the value
+ * @throws BindingException
+ * @throws ParseException
+ */
+ public Object parseValueDefinition(String text) throws DataTypeSyntaxError, BindingException
+ {
+ try {
+ DataValueRepository repo = new DataValueRepository();
+ String name = repo.addValueDefinition("value : Variant = " + text);
+ MutableVariant value = repo.get(name);
+ return value.getValue(this);
+ } catch (AdaptException e) {
+ throw new BindingException(e);
+ }
+ }
+
+ /**
+ * Print a value as a data value repository.
+ *
+ * Datavalue notation
+ *
+ * @param value
+ * @param singleLine
+ * @return the value in ascii format
+ * @throws IOException
+ * @throws BindingException
+ */
+ 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();
+ }
+
+ /**
+ * Print a value to an appendable using data value notation.
+ *
+ * Datavalue Notation
+ *
+ * @param value
+ * @param out
+ * @param singleLine
+ * @throws IOException
+ * @throws BindingException
+ */
+ public void printValue(Object value, Appendable out, DataValueRepository valueRepository, boolean singleLine) throws IOException, BindingException
+ {
+ DataValuePrinter writable = new DataValuePrinter( out, valueRepository );
+ writable.setFormat(singleLine ? PrintFormat.SINGLE_LINE : PrintFormat.MULTI_LINE);
+ writable.print(this, value);
+ }
+
+ /**
+ * Calculate Hash code for a Data Value.
+ *
+ * Type Hash Function
+ * ------------------------------------------------
+ * Boolean true=1231, false=1237
+ * Integer value
+ * Long lower 32-bits ^ higher 32-bits
+ * Float IEEE 754 floating-point "single format" bit layout as is.
+ * Double lower 32-bits ^ higher 32-bits of IEEE 754 floating-point "double format" bit layout.
+ * Optional no value = 0, else hash(value)
+ * Array int result = 1; for (int element : array) result = 31 * result + hash(element);
+ * Record int result = 3; for (field : record) result = 31 * result + hash(field) (See *);
+ * Variant hash(type) + hash(value)
+ * Union tag + hash(value)
+ * Map int result = 0; for (entry : map) result += hash(key) ^ hash(value);
+ * Byte value
+ *
+ * *) In case of recursion, the function (hash or compareTo) will not enter same value twice. 0 is returned in such a case.
+ *
+ * @param value
+ * @return hash value
+ * @throws BindingException
+ */
+ public int hashValue(Object value) throws BindingException
+ {
+ return deepHashValue(value, null);
+ }
+
+ /**
+ * Calculate hash value
+ *
+ * @param value
+ * @param hashedObjects collection of already hashed object or optionally null
+ * @return hash value
+ */
+ public abstract int deepHashValue(Object value, IdentityHashMap hashedObjects) throws BindingException;
+
+ /**
+ * Compares its two data values for order. Returns a negative integer,
+ * zero, or a positive integer as the first argument is less than, equal
+ * to, or greater than the second.
+ *
+ * The implementor must also ensure that the relation is transitive:
+ * ((compare(x, y)>0) && (compare(y, z)>0))
implies
+ * compare(x, z)>0
.
+ *
+ * Finally, the implementor must ensure that compare(x, y)==0
+ * implies that sgn(compare(x, z))==sgn(compare(y, z))
for all
+ * z
.
+ *
+ * The comparison function is defined at
+ * http://dev.simantics.org/index.php/Org.simantics.databoard_Manual#CompareTo_and_Equals
+ *
+ * Note, comparing 2 different number types will not result a value comparison.
+ * Instead values have the following type precedence ByteType, IntegerType, LongType,
+ * FloatType, and the highest DoubleType.
+ *
+ * @param o1 the first object to be compared.
+ * @param o2 the second object to be compared.
+ * @return a negative integer, zero, or a positive integer as the
+ * 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
+ {
+ if (o1==o2) return 0;
+ if (!isInstance(o1)) throw new IllegalArgumentException(o1+" is not of expected class");
+ if (!isInstance(o2)) throw new IllegalArgumentException(o2+" is not of expected class");
+
+ try {
+ return deepCompare(o1, o2, null);
+ } catch (BindingException e) {
+ throw new RuntimeBindingException(e);
+ }
+ }
+
+ /**
+ * Compare two Java Objects of this binding for equality.
+ *
+ * @param o1
+ * @param o2
+ * @return true if equal
+ * @throws RuntimeBindingException
+ */
+ public boolean equals(Object o1, Object o2)
+ throws RuntimeBindingException
+ {
+ int dif = compare(o1, o2);
+ return dif == 0;
+ }
+
+ /**
+ * Make a complete copy of a the java object. Bindings that handle immutable values
+ * may return the same instance, others will guarantee a complete copy.
+ *
+ * Note, this is a generic implementation, override for better performance.
+ *
+ * @param o to be cloned
+ * @return a complete copy
+ * @throws AdapterConstructionException
+ * @throws AdaptException
+ */
+ public Object clone(Object o) throws AdaptException {
+ try {
+ return Bindings.adapterFactory.getAdapter(this, this, false, true).adapt(o);
+ } catch (AdapterConstructionException e) {
+ // Should not occur
+ throw new AdaptException(e);
+ }
+ }
+
+ public Object cloneUnchecked(Object o) throws RuntimeAdaptException {
+ try {
+ return Bindings.adapterFactory.getAdapter(this, this, false, true).adapt(o);
+ } catch (AdaptException e) {
+ throw new RuntimeAdaptException(e);
+ } catch (AdapterConstructionException e) {
+ throw new RuntimeAdaptException(new AdaptException(e));
+ }
+ }
+
+
+ public abstract int deepCompare(Object o1, Object o2, Set> compareHistory)
+ throws BindingException;
+
+ /**
+ * Create a value with valid default values.
+ *
+ * Boolean false
+ * Byte, Integer, Long 0
+ * Float, Double 0.0
+ * String "" (may not follow pattern)
+ * Optional *novalue*
+ * Union tag 0
+ * Record each field with default value
+ * Array min range number of elements
+ * Map no entries
+ * Variant Optional with no value
+ *
+ * @return default value
+ */
+ public Object createDefault()
+ throws BindingException
+ {
+ try {
+ return accept(new DefaultValue());
+ } catch (RuntimeBindingException e) {
+ throw e.getCause();
+ }
+ }
+
+ public Object createDefaultUnchecked()
+ throws RuntimeBindingException
+ {
+ 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();
+ }
+ }
+
+
+ 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();
+ }
+}