Define OpaqueData/OpaqueObject hashCode/equals 46/81646/2
authorRobert Varga <robert.varga@pantheon.tech>
Fri, 12 Apr 2019 02:09:33 +0000 (04:09 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 14 Apr 2019 15:29:37 +0000 (17:29 +0200)
This adds baseline hashCode/equals specification, so that we do not
rely on identity for comparisons.

JIRA: MDSAL-439
Change-Id: I41291935a0402f6fce13b6e49531d85e2fc905d5
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
(cherry picked from commit 4c82af2a3ed7b1dc78b8e50b749dbd2b0c82268c)

binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueData.java [new file with mode: 0644]
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueObject.java [new file with mode: 0644]
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueData.java
binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueObject.java

diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueData.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueData.java
new file mode 100644 (file)
index 0000000..fcefc5c
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  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
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Utility class for {@link OpaqueData} implementations. This class provides baseline implementation of
+ * {@link #hashCode()} and {@link #equals(Object)} as specified by {@link OpaqueData}. For cases where the object
+ * model's objects do not provide a usable implementation of hashCode/equals, this class is expected to be subclassed
+ * to provide alternative implementation of {@link #dataHashCode()} and {@link #dataEquals(Object)} methods. Such
+ * class should be made public in a convenient place. Note such customized methods are required to maintain consistency
+ * between hashCode and equals, as well as the <i>reflexive</i>, <i>symmetric</i>, <i>transitive</i> and
+ * <i>consistent</i> properties as detailed in {@link Object#equals(Object)}.
+ *
+ * @param <T> Data object model type
+ */
+@Beta
+public abstract class AbstractOpaqueData<T> implements OpaqueData<T> {
+    @Override
+    public final int hashCode() {
+        return 31 * getObjectModel().hashCode() + dataHashCode();
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof OpaqueData)) {
+            return false;
+        }
+        final OpaqueData<?> other = (OpaqueData<?>) obj;
+        return getObjectModel().equals(other.getObjectModel()) && dataEquals(other.getData());
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this).add("objectModel", getObjectModel())).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("data", getData());
+    }
+
+    /**
+     * Determine hashCode of the data. The default implementation uses the data object's {@code hashCode} method.
+     *
+     * @return Hash code value of data
+     */
+    protected int dataHashCode() {
+        return getData().hashCode();
+    }
+
+    protected boolean dataEquals(final @NonNull Object otherData) {
+        return getData().equals(otherData);
+    }
+}
diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueObject.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueObject.java
new file mode 100644 (file)
index 0000000..2115e9c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  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
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.MoreObjects;
+import com.google.common.base.MoreObjects.ToStringHelper;
+import org.eclipse.jdt.annotation.NonNull;
+
+/**
+ * Utility base class for {@link OpaqueObject} implementations. This class provides baseline implementation of
+ * {@link #hashCode()} and {@link #equals(Object)} as specified by {@link OpaqueObject}.
+ *
+ * @param <T> Implemented OpaqueObject type
+ */
+@Beta
+public abstract class AbstractOpaqueObject<T extends OpaqueObject<T>> implements OpaqueObject<T> {
+    @Override
+    public final int hashCode() {
+        return 31 * getImplementedInterface().hashCode() + valueHashCode();
+    }
+
+    @Override
+    public final boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof OpaqueObject)) {
+            return false;
+        }
+        final OpaqueObject<?> other = (OpaqueObject<?>) obj;
+        return getImplementedInterface().equals(other.getImplementedInterface()) && valueEquals(other.getValue());
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(MoreObjects.toStringHelper(this)
+            .add("implementedInterface", getImplementedInterface())).toString();
+    }
+
+    protected ToStringHelper addToStringAttributes(final ToStringHelper helper) {
+        return helper.add("value", getValue());
+    }
+
+    protected boolean valueEquals(final @NonNull OpaqueData<?> otherValue) {
+        return getValue().equals(otherValue);
+    }
+
+    protected int valueHashCode() {
+        return getValue().hashCode();
+    }
+}
index 6825b83ccff2b6bdd354b4e52ee2515a6905600d..c96e4d89a0cd4ead8de3a74fd1248f5cccf251cd 100644 (file)
@@ -32,4 +32,26 @@ public interface OpaqueData<T> extends Immutable {
      * @return Data held in this object.
      */
     @NonNull T getData();
+
+    /**
+     * The hash code of any {@link OpaqueData} instance is defined by the combination of its object model and the data
+     * it holds. This is inherently object-model-specific hence different OpaqueData defined by distinct object models
+     * will result in differing hash codes. This implies that node with differing object models cannot compare as equal.
+     * See {@link AbstractOpaqueData#hashCode()} for canonical implementation.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    int hashCode();
+
+    /**
+     * Compare this object to another object. The comparison needs to take into account {@link #getObjectModel()}
+     * first and then follow comparison on {@link #getData()}. For canonical algorithm please refer to
+     * {@link AbstractOpaqueData#equals(Object)}.
+     *
+     * @param obj the reference object with which to compare.
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    @Override
+    boolean equals(Object obj);
 }
index 1bedda44adceb7b3f51a350b38b2d7900c2c4b54..669cb0dc1d48f351bdb871fff353efaabc94dc8f 100644 (file)
@@ -20,6 +20,9 @@ import com.google.common.annotations.Beta;
  * of {@link #getImplementedInterface()} bound to itself. The value is communicated through {@link #getValue()}, which
  * is only an encapsulation holding information about the object model and the data in that object model.
  *
+ * <p>
+ * Implementations are strongly encouraged to use {@link AbstractOpaqueObject} as their base implementation class.
+ *
  * @param <T> Generated interface
  */
 @Beta
@@ -27,4 +30,24 @@ public interface OpaqueObject<T extends OpaqueObject<T>> extends BindingObject,
         ValueAware<OpaqueData<?>> {
     @Override
     Class<T> getImplementedInterface();
+
+    /**
+     * Hash code value of this object. This is a combination of {@link #getImplementedInterface()} and the value being
+     * held. The canonical computation algorithm is defined in {@link AbstractOpaqueObject#hashCode()}.
+     *
+     * @return a hash code value for this object.
+     */
+    @Override
+    int hashCode();
+
+    /**
+     * Compare this object to another object. The comparison needs to take into account
+     * {@link #getImplementedInterface()} first and then follow comparison on {@link #getValue()}. For canonical
+     * algorithm please refer to {@link AbstractOpaqueObject#equals(Object)}.
+     *
+     * @param obj the reference object with which to compare.
+     * @return {@code true} if this object is the same as the obj argument; {@code false} otherwise.
+     */
+    @Override
+    boolean equals(Object obj);
 }