Fix ImmutableLeaf(SetEntry)Node.toString() 89/109789/2
authorRobert Varga <robert.varga@pantheon.tech>
Mon, 15 Jan 2024 22:45:54 +0000 (23:45 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 16 Jan 2024 00:17:16 +0000 (01:17 +0100)
Allow the class identity of AbstraectNormalizedNode subclass to be
overridden, so that we can have externally-invisible class hierarchy.

Since we want to assert stable toString(), this patch also addresses
NodeWithValue.toString().

In both cases byte[] values are encoded with 'b64:' prefix followed by
base-64 encoded value.

JIRA: YANGTOOLS-1562
JIRA: YANGTOOLS-1563
Change-Id: I686eeb4769d9ebdcf21f7c11b314ccc85fe943fb
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractLeafNode.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractLeafSetEntryNode.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractNormalizedNode.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractNormalizedSimpleValueNode.java
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractValueNode.java [new file with mode: 0644]
data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/ValueNode.java
data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/impl/ImmutableLeafNode.java
data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/impl/ImmutableLeafSetEntryNode.java
data/yang-data-spi/src/test/java/org/opendaylight/yangtools/yang/data/spi/node/impl/YT1562Test.java [new file with mode: 0644]

index 7a533965e334d13095660afb1bac8eeb54d7534c..2cc4d66aa9327eb1aaf8abbef0c5a57390705cab 100644 (file)
@@ -25,6 +25,7 @@ import java.lang.invoke.VarHandle;
 import java.lang.reflect.Array;
 import java.util.AbstractMap.SimpleImmutableEntry;
 import java.util.Arrays;
+import java.util.Base64;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.Iterator;
@@ -484,8 +485,8 @@ public abstract sealed class YangInstanceIdentifier
             return 0;
         }
 
-        if (byte[].class.equals(value.getClass())) {
-            return Arrays.hashCode((byte[]) value);
+        if (value instanceof byte[] bytes) {
+            return Arrays.hashCode(bytes);
         }
 
         if (value.getClass().isArray()) {
@@ -498,7 +499,7 @@ public abstract sealed class YangInstanceIdentifier
             return hash;
         }
 
-        return Objects.hashCode(value);
+        return value.hashCode();
     }
 
     private int loadHashCode() {
@@ -948,16 +949,14 @@ public abstract sealed class YangInstanceIdentifier
         @Override
         @SuppressWarnings("checkstyle:equalsHashCode")
         public boolean equals(final Object obj) {
-            if (!super.equals(obj)) {
-                return false;
-            }
-            final NodeWithValue<?> other = (NodeWithValue<?>) obj;
-            return Objects.deepEquals(value, other.value);
+            return super.equals(obj) && Objects.deepEquals(value, ((NodeWithValue<?>) obj).value);
         }
 
         @Override
         public String toString() {
-            return super.toString() + '[' + value + ']';
+            final var str = super.toString();
+            return value instanceof byte[] bytes ? str + "[b64:" + Base64.getEncoder().encodeToString(bytes) + ']'
+                : str + '[' + value + ']';
         }
 
         @Override
index 4c27cc62cfaa15eef5cb20e14e1af95829cffcb0..229eef9e4bb298bb385fb2a3eb7194e92966b0cc 100644 (file)
@@ -12,7 +12,7 @@ import org.eclipse.jdt.annotation.NonNull;
 /**
  * Abstract base class for {@link LeafNode} implementations.
  */
-public abstract non-sealed class AbstractLeafNode<T> extends AbstractNormalizedSimpleValueNode<LeafNode<T>, T>
+public abstract non-sealed class AbstractLeafNode<T> extends AbstractValueNode<LeafNode<T>, T>
         implements LeafNode<T> {
     @Override
     @SuppressWarnings("unchecked")
index 54ba7548553e773df186037043177a8d25c3cc20..c7fb49adf6c3203790ea38ccb5be49c01b7e77cb 100644 (file)
@@ -13,7 +13,7 @@ import org.eclipse.jdt.annotation.NonNull;
  * Abstract base class for {@link LeafSetEntryNode} implementations.
  */
 public abstract non-sealed class AbstractLeafSetEntryNode<T>
-        extends AbstractNormalizedSimpleValueNode<LeafSetEntryNode<T>, T> implements LeafSetEntryNode<T> {
+        extends AbstractValueNode<LeafSetEntryNode<T>, T> implements LeafSetEntryNode<T> {
     @Override
     @SuppressWarnings("unchecked")
     protected final Class<LeafSetEntryNode<T>> implementedType() {
index 6534b469022e7494b8ed366e45af778fdfa1c10f..2da42055b1fe9f62de9e0f210e13e45cc3d5130e 100644 (file)
@@ -47,11 +47,26 @@ public abstract sealed class AbstractNormalizedNode<T extends NormalizedNode> im
 
     @Override
     public final String toString() {
-        return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
+        return addToStringAttributes(MoreObjects.toStringHelper(toStringClass())).toString();
+    }
+
+    /**
+     * Return the {@link #toString()} class identity of this object. Default implementation defers to
+     * {@link #getClass()}. Override may be needed to hide implementation-internal class hierarchy -- for example if
+     * providing different implementations of {@link LeafNode} for {@code String} and {@code byte[]} values.
+     *
+     * @return the {@link #toString()} class identity of this object
+     */
+    protected @NonNull Class<?> toStringClass() {
+        return getClass();
+    }
+
+    @NonNull Object toStringBody() {
+        return body();
     }
 
     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
-        return toStringHelper.add("name", name()).add("body", body());
+        return toStringHelper.add("name", name()).add("body", toStringBody());
     }
 
     protected abstract @NonNull Class<T> implementedType();
index 24d44fca561a20ff78e48a3f4530c22ee0fc786d..f54e07cdc5fd36944002666164dbb5404632f6fd 100644 (file)
@@ -11,7 +11,7 @@ import java.util.Objects;
 
 abstract sealed class AbstractNormalizedSimpleValueNode<N extends NormalizedNode, V>
         extends AbstractNormalizedValueNode<N, V>
-        permits AbstractAnydataNode, AbstractAnyxmlNode, AbstractLeafNode, AbstractLeafSetEntryNode {
+        permits AbstractAnydataNode, AbstractAnyxmlNode, AbstractValueNode {
     @Override
     protected final int valueHashCode() {
         return value().hashCode();
diff --git a/data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractValueNode.java b/data/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/AbstractValueNode.java
new file mode 100644 (file)
index 0000000..49f5259
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2024 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.data.api.schema;
+
+import java.util.Base64;
+
+abstract sealed class AbstractValueNode<N extends ValueNode<V>, V> extends AbstractNormalizedSimpleValueNode<N, V>
+        implements ValueNode<V> permits AbstractLeafNode, AbstractLeafSetEntryNode {
+    @Override
+    final Object toStringBody() {
+        final var body = body();
+        return body instanceof byte[] bytes ? "b64:" + Base64.getEncoder().encodeToString(bytes) : body;
+    }
+}
index 89e17af894a9c28d4938e73121f9d871e42ef056..95828aafac8f16455a1632d1cc2cf1cda64591f8 100644 (file)
@@ -12,7 +12,7 @@ package org.opendaylight.yangtools.yang.data.api.schema;
  *
  * @param <V> Value of node, which needs to be a well-published simple value type.
  */
-public sealed interface ValueNode<V> extends NormalizedNode permits LeafNode, LeafSetEntryNode {
+public sealed interface ValueNode<V> extends NormalizedNode permits LeafNode, LeafSetEntryNode, AbstractValueNode {
     /**
      * {@inheritDoc}
      *
index 89a31aa95bfdbca3671098a38a359c4c2bd5753b..f59a354ee4531dda73045c45e5f08399f632b11c 100644 (file)
@@ -65,4 +65,9 @@ public abstract sealed class ImmutableLeafNode<T> extends AbstractLeafNode<T> {
     protected final T value() {
         return value;
     }
+
+    @Override
+    protected final Class<?> toStringClass() {
+        return ImmutableLeafNode.class;
+    }
 }
index 39ab9acaa87f6a77101e6eef6d02c3992071d12b..8d102664a4fe3a895c2aeb1033b795f8ec8573f0 100644 (file)
@@ -83,4 +83,9 @@ public abstract sealed class ImmutableLeafSetEntryNode<T> extends AbstractLeafSe
     protected final T value() {
         return name.getValue();
     }
+
+    @Override
+    protected final Class<?> toStringClass() {
+        return ImmutableLeafSetEntryNode.class;
+    }
 }
diff --git a/data/yang-data-spi/src/test/java/org/opendaylight/yangtools/yang/data/spi/node/impl/YT1562Test.java b/data/yang-data-spi/src/test/java/org/opendaylight/yangtools/yang/data/spi/node/impl/YT1562Test.java
new file mode 100644 (file)
index 0000000..63d6b90
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2024 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.data.spi.node.impl;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+
+class YT1562Test {
+    @Test
+    void regualarLeafToString() {
+        assertEquals("ImmutableLeafNode{name=(test)test, body=42}",
+            ImmutableLeafNode.of(new NodeIdentifier(QName.create("test", "test")), 42).toString());
+    }
+
+    @Test
+    void binaryLeafToString() {
+        assertEquals("ImmutableLeafNode{name=(test)test, body=b64:Kis=}",
+            ImmutableLeafNode.of(new NodeIdentifier(QName.create("test", "test")), new byte[] { 42, 43 }).toString());
+    }
+
+    @Test
+    void regualarLeafSetEntryToString() {
+        assertEquals("ImmutableLeafSetEntryNode{name=(test)test[42], body=42}",
+            ImmutableLeafSetEntryNode.of(new NodeWithValue<>(QName.create("test", "test"), 42)).toString());
+    }
+
+    @Test
+    void binaryLeafSetEntryToString() {
+        assertEquals("ImmutableLeafSetEntryNode{name=(test)test[b64:Kis=], body=b64:Kis=}",
+            ImmutableLeafSetEntryNode.of(new NodeWithValue<>(QName.create("test", "test"), new byte[] { 42, 43 }))
+                .toString());
+    }
+}