From ad95e065272618519fc5fb0016f47b5b53ead264 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 12 Apr 2019 04:09:33 +0200 Subject: [PATCH] Define OpaqueData/OpaqueObject hashCode/equals 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 (cherry picked from commit 4c82af2a3ed7b1dc78b8e50b749dbd2b0c82268c) --- .../yang/binding/AbstractOpaqueData.java | 66 +++++++++++++++++++ .../yang/binding/AbstractOpaqueObject.java | 57 ++++++++++++++++ .../yangtools/yang/binding/OpaqueData.java | 22 +++++++ .../yangtools/yang/binding/OpaqueObject.java | 23 +++++++ 4 files changed, 168 insertions(+) create mode 100644 binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueData.java create mode 100644 binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueObject.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 index 0000000000..fcefc5c822 --- /dev/null +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueData.java @@ -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 reflexive, symmetric, transitive and + * consistent properties as detailed in {@link Object#equals(Object)}. + * + * @param Data object model type + */ +@Beta +public abstract class AbstractOpaqueData implements OpaqueData { + @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 index 0000000000..2115e9c621 --- /dev/null +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/AbstractOpaqueObject.java @@ -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 Implemented OpaqueObject type + */ +@Beta +public abstract class AbstractOpaqueObject> implements OpaqueObject { + @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(); + } +} diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueData.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueData.java index 6825b83ccf..c96e4d89a0 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueData.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueData.java @@ -32,4 +32,26 @@ public interface OpaqueData 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); } diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueObject.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueObject.java index 1bedda44ad..669cb0dc1d 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueObject.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/OpaqueObject.java @@ -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. * + *

+ * Implementations are strongly encouraged to use {@link AbstractOpaqueObject} as their base implementation class. + * * @param Generated interface */ @Beta @@ -27,4 +30,24 @@ public interface OpaqueObject> extends BindingObject, ValueAware> { @Override Class 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); } -- 2.36.6