From: Konstantin.Nosach Date: Fri, 12 Feb 2021 12:31:57 +0000 (+0200) Subject: Improve NormalizedNode formatting capabilities X-Git-Tag: v7.0.0~9 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=be5dd6ce039a63a8e14f1d7dd2320f49f4aa83d6;p=yangtools.git Improve NormalizedNode formatting capabilities Add NormalizedNode.prettyTree(), which acts as a conduit to a human-readable String representing the tree. The tree is hidden behind a toString()/Supplier, so it is usable with logging. Since this is a generally-useful concept, which can span different kinds of structures, capture it as PrettyTree/PrettyTreeAware as well. JIRA: YANGTOOLS-1203 Change-Id: I6f2c98bc3048a086063846699ec73e0f2ac02b33 Signed-off-by: Kostiantyn Nosach Signed-off-by: Ivan Hrasko Signed-off-by: Robert Varga --- diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTree.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTree.java new file mode 100644 index 0000000000..f9dbe5957a --- /dev/null +++ b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTree.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2021 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.concepts; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; +import java.util.function.Supplier; +import org.eclipse.jdt.annotation.NonNull; + +/** + * A capture of a tree-like construct, which can be formatted into a pretty-printed tree. The string can be acquired + * via {@link #get()}. + * + *

+ * This concept is purposefully designed as an abstract class which defers its {@link #toString()} to {@link #get()}, as + * it allows convenient and light-weight use with logging: + * + *

+ *   
+ *     PrettyTreeAware treeLike;
+ *     LOG.debug("Tree is {}", treeLike.prettyTree());
+ *   
+ * 
+ */ +@Beta +public abstract class PrettyTree implements Supplier { + @Override + public @NonNull String get() { + final StringBuilder sb = new StringBuilder(); + appendTo(sb, 0); + return sb.toString(); + } + + @Override + public final @NonNull String toString() { + return get(); + } + + /** + * Format this object into specified {@link StringBuilder} starting at specified initial depth. + * + * @param sb Target {@link StringBuilder} + * @param depth Initial nesting depth + * @throws NullPointerException if {@code sb} is null + * @throws IllegalArgumentException if {@code depth} is negative + */ + public abstract void appendTo(StringBuilder sb, int depth); + + /** + * Append a number of spaces equivalent to specified tree nesting depth into the specified {@link StringBuilder}. + * + * @param sb Target {@link StringBuilder} + * @param depth Nesting depth + * @throws NullPointerException if {@code sb} is null + * @throws IllegalArgumentException if {@code depth} is negative + */ + protected static final void appendIndent(final StringBuilder sb, final int depth) { + checkArgument(depth >= 0, "Invalid depth %s", depth); + PrettyTreeIndent.indent(sb, depth); + } +} diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeAware.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeAware.java new file mode 100644 index 0000000000..7c994537ec --- /dev/null +++ b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeAware.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021 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.concepts; + +import org.eclipse.jdt.annotation.NonNullByDefault; + +/** + * Utility interface to bridge structures which can be formatted via {@link PrettyTree}. + */ +@NonNullByDefault +public interface PrettyTreeAware { + /** + * Return a {@link PrettyTree} view of this object. + * + * @return A {@link PrettyTree}. + */ + PrettyTree prettyTree(); +} diff --git a/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeIndent.java b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeIndent.java new file mode 100644 index 0000000000..2cfaa627fe --- /dev/null +++ b/common/concepts/src/main/java/org/opendaylight/yangtools/concepts/PrettyTreeIndent.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2021 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.concepts; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Indentation handling for {@link PrettyTree}. This class is split out to defer initialization of the string table -- + * it might never be used after all. + * + *

+ * We want to be formatting strings quickly and a lot of that can very easily be dominated by dealing with indents. + * To deal with that we pre-compute a few indentation strings and then append them directly using a specialized + * method. We allow tuning the default indentation at runtime, but choose a fixed string table size. + */ +final class PrettyTreeIndent { + private static final Logger LOG = LoggerFactory.getLogger(PrettyTreeIndent.class); + private static final int DEFAULT_INDENT = 4; + private static final int INDENT_STRINGS_SIZE = 16; + private static final String[] INDENT_STRINGS; + + static { + int indent = Integer.getInteger("org.opendaylight.yangtools.concepts.pretty-tree-indent", DEFAULT_INDENT); + if (indent < 1) { + LOG.warn("Invalid pretty-tree-indent {}, using {} instead", indent, DEFAULT_INDENT); + indent = DEFAULT_INDENT; + } else if (indent != DEFAULT_INDENT) { + LOG.info("Using pretty-tree-indent {}", indent); + } + + final String one = " ".repeat(indent); + final String[] strings = new String[INDENT_STRINGS_SIZE]; + for (int i = 0; i < INDENT_STRINGS_SIZE; i++) { + strings[i] = one.repeat(i).intern(); + } + INDENT_STRINGS = strings; + } + + private PrettyTreeIndent() { + // Hidden on purpose + } + + static void indent(final StringBuilder sb, final int depth) { + int remaining = depth; + while (remaining >= INDENT_STRINGS_SIZE) { + sb.append(INDENT_STRINGS[INDENT_STRINGS_SIZE - 1]); + remaining -= INDENT_STRINGS_SIZE; + } + sb.append(INDENT_STRINGS[remaining]); + } +} diff --git a/data/yang-data-spi/src/main/java/module-info.java b/data/yang-data-spi/src/main/java/module-info.java index 35a9c20ecb..189a2267ef 100644 --- a/data/yang-data-spi/src/main/java/module-info.java +++ b/data/yang-data-spi/src/main/java/module-info.java @@ -13,6 +13,7 @@ module org.opendaylight.yangtools.yang.data.spi { requires transitive org.opendaylight.yangtools.yang.data.api; requires transitive org.opendaylight.yangtools.concepts; requires org.opendaylight.yangtools.util; + requires org.opendaylight.yangtools.yang.common; requires org.slf4j; // Annotations diff --git a/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/AbstractNormalizedNode.java b/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/AbstractNormalizedNode.java index 571573142d..0796b868b3 100644 --- a/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/AbstractNormalizedNode.java +++ b/data/yang-data-spi/src/main/java/org/opendaylight/yangtools/yang/data/spi/node/AbstractNormalizedNode.java @@ -13,6 +13,7 @@ import com.google.common.base.MoreObjects.ToStringHelper; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.AbstractIdentifiable; import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.concepts.PrettyTree; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -29,6 +30,11 @@ public abstract class AbstractNormalizedNode container = (NormalizedNodeContainer) node; + sb.append("= {"); + + final Iterator it = container.body().iterator(); + if (it.hasNext()) { + final int childIndent = depth + 1; + do { + sb.append('\n'); + appendNode(sb, childIndent, currentNamespace, it.next()); + } while (it.hasNext()); + + sb.append('\n'); + appendIndent(sb, depth); + } + sb.append('}'); + } else if (node instanceof ValueNode) { + sb.append("= "); + final Object value = node.body(); + if (value instanceof byte[]) { + sb.append("(byte[])").append(Base64.getEncoder().encodeToString((byte[]) value)); + } else if (value instanceof String) { + appendString(sb, (String) value); + } else { + sb.append(value); + } + } else if (node instanceof ForeignDataNode) { + final ForeignDataNode data = (ForeignDataNode) node; + sb.append("= (").append(data.bodyObjectModel().getName()).append(')'); + + final Object body = data.body(); + if (body instanceof PrettyTreeAware) { + sb.append(" {\n"); + ((PrettyTreeAware) body).prettyTree().appendTo(sb, depth + 1); + appendIndent(sb, depth); + sb.append('}'); + } + } else { + throw new IllegalStateException("Unhandled node " + node); + } + } + + private static boolean appendNamespace(final StringBuilder sb, final QNameModule parent, + final QNameModule current) { + if (!current.equals(parent)) { + sb.append('(').append(current.getNamespace()); + final Optional rev = current.getRevision(); + if (rev.isPresent()) { + sb.append('@').append(rev.orElseThrow()); + } + sb.append(')'); + return true; + } + return false; + } + + private static void appendString(final StringBuilder sb, final String str) { + // TODO: do some escaping: '\r' '\n' '"' '\\' to make things even more zazzy + sb.append('"').append(str).append('"'); + } +} diff --git a/yang/rfc8528-data-util/pom.xml b/yang/rfc8528-data-util/pom.xml index 4a0a5d193f..331b8a2b87 100644 --- a/yang/rfc8528-data-util/pom.xml +++ b/yang/rfc8528-data-util/pom.xml @@ -38,6 +38,10 @@ org.opendaylight.yangtools yang-data-api + + org.opendaylight.yangtools + yang-data-spi + org.opendaylight.yangtools yang-model-api diff --git a/yang/rfc8528-data-util/src/main/java/module-info.java b/yang/rfc8528-data-util/src/main/java/module-info.java index 38d6d6c405..77de84111b 100644 --- a/yang/rfc8528-data-util/src/main/java/module-info.java +++ b/yang/rfc8528-data-util/src/main/java/module-info.java @@ -20,6 +20,7 @@ module org.opendaylight.yangtools.rfc8528.data.util { requires org.opendaylight.yangtools.rfc8528.model.api; requires org.opendaylight.yangtools.yang.common; + requires org.opendaylight.yangtools.yang.data.spi; requires org.slf4j; // Annotations diff --git a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rfc8528/data/util/ImmutableMountPointNode.java b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rfc8528/data/util/ImmutableMountPointNode.java index fbdf318e92..3b937644d3 100644 --- a/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rfc8528/data/util/ImmutableMountPointNode.java +++ b/yang/rfc8528-data-util/src/main/java/org/opendaylight/yangtools/rfc8528/data/util/ImmutableMountPointNode.java @@ -15,12 +15,14 @@ import java.util.Collection; import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.concepts.AbstractIdentifiable; import org.opendaylight.yangtools.concepts.Immutable; +import org.opendaylight.yangtools.concepts.PrettyTree; import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier; import org.opendaylight.yangtools.rfc8528.data.api.MountPointNode; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.spi.node.NormalizedNodePrettyTree; @Beta public final class ImmutableMountPointNode extends AbstractIdentifiable @@ -55,6 +57,11 @@ public final class ImmutableMountPointNode extends AbstractIdentifiable { +public interface NormalizedNode extends Identifiable, PrettyTreeAware { @Override // We override here, so that NormalizedNode.getIdentifier() has fewer implementations PathArgument getIdentifier(); diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractPrettyTreeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractPrettyTreeTest.java new file mode 100644 index 0000000000..0e723998fd --- /dev/null +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/AbstractPrettyTreeTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2021 PANTHEON.tech s.r.o. 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.impl.schema.tree; + +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder; + +import java.util.List; +import java.util.Set; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserLeafSetNode; +import org.opendaylight.yangtools.yang.data.api.schema.UserMapNode; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; + +public abstract class AbstractPrettyTreeTest { + protected static final QName ROOT_QNAME = QName.create( + "urn:opendaylight:controller:sal:dom:store:test", "2014-03-13", "root"); + protected static final QName ANOTHER_QNAME = QName.create( + "urn:opendaylight:controller:sal:dom:store:another", "another"); + + protected static final QName LIST_A_QNAME = QName.create(ROOT_QNAME, "list-a"); + protected static final QName LEAF_A_QNAME = QName.create(ROOT_QNAME, "leaf-a"); + protected static final QName LIST_B_QNAME = QName.create(ROOT_QNAME, "list-b"); + protected static final QName LEAF_B_QNAME = QName.create(ROOT_QNAME, "leaf-b"); + + protected static final QName CHOICE_QNAME = QName.create(ROOT_QNAME, "choice"); + protected static final QName AUGMENT_QNAME = QName.create(ROOT_QNAME, "augment"); + + protected static final QName LIST_ANOTHER_NAMESPACE_QNAME = QName.create(ANOTHER_QNAME, + "list-from-another-namespace"); + protected static final QName LEAF_ANOTHER_NAMESPACE_QNAME = QName.create(ANOTHER_QNAME, + "leaf-from-another-namespace"); + + protected static final QName LEAF_QNAME = QName.create(ROOT_QNAME, "leaf"); + protected static final QName LEAF_SET_QNAME = QName.create(ROOT_QNAME, "leaf-set"); + + protected static final QName USER_LEAF_SET_QNAME = QName.create(ROOT_QNAME, "user-leaf-set"); + protected static final QName USER_MAP_QNAME = QName.create(ROOT_QNAME, "user-map"); + protected static final QName USER_MAP_ENTRY_QNAME = QName.create(ROOT_QNAME, "user-map-entry"); + + protected static final QName UNKEYED_LIST_QNAME = QName.create(ROOT_QNAME, + "unkeyed-list"); + protected static final QName UNKEYED_LIST_ENTRY_QNAME = QName.create(ROOT_QNAME, + "unkeyed-list-entry"); + protected static final QName UNKEYED_LIST_LEAF_QNAME = QName.create(ROOT_QNAME, + "unkeyed-list-leaf"); + + protected static final QName ANY_DATA_QNAME = QName.create(ROOT_QNAME, "any-data"); + + /** + * Return a test node. + * + *

+     * root
+     *     list-a
+     *          leaf-a "foo"
+     *     list-a
+     *          leaf-a "bar"
+     *          list-b
+     *                  leaf-b "one"
+     *          list-b
+     *                  leaf-b "two"
+     *     choice
+     *          augment
+     *                  augmented-leaf "Augmented leaf value"
+     *     another
+     *          list-from-another-namespace
+     *               leaf-from-another-namespace "Leaf from another namespace value"
+     *     leaf "Leaf value"
+     *     leaf-set "Leaf set value"
+     *     user-leaf-set "User leaf set value"
+     *     user-map
+     *          user-map-entry "User map entry value"
+     *     unkeyed-list
+     *          unkeyed-list-entry
+     *               unkeyed-list-leaf "Unkeyed list leaf value"
+     *     any-data "Any data value"
+     *
+     * 
+ * + * @return A test node + */ + protected static NormalizedNode createContainerNode() { + return ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new NodeIdentifier(ROOT_QNAME)) + .withChild(createMapNode()) + .withChild(createChoiceNode()) + .withChild(createContainerFromAnotherNamespace()) + .withChild(createLeafNode()) + .withChild(createLeafSetNode()) + .withChild(createUserLeafSetNode()) + .withChild(createUserMapNode()) + .withChild(createUnkeyedListNode()) + .withChild(createAnyDataNode()) + .build(); + } + + protected static MapNode createMapNode() { + return mapNodeBuilder(LIST_A_QNAME) + .withChild(mapEntry(LIST_A_QNAME, LEAF_A_QNAME, "foo")) + .withChild(createMapEntryNode()).build(); + } + + protected static MapEntryNode createMapEntryNode() { + return mapEntryBuilder(LIST_A_QNAME, LEAF_A_QNAME, "bar") + .withChild(mapNodeBuilder(LIST_B_QNAME) + .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, "one")) + .withChild(mapEntry(LIST_B_QNAME, LEAF_B_QNAME, "two")) + .build()).build(); + } + + protected static ChoiceNode createChoiceNode() { + return Builders.choiceBuilder() + .withNodeIdentifier(NodeIdentifier.create(CHOICE_QNAME)) + .withChild(createAugmentationNode()) + .build(); + } + + protected static AugmentationNode createAugmentationNode() { + return Builders.augmentationBuilder() + .withNodeIdentifier(AugmentationIdentifier + .create(Set.of(AUGMENT_QNAME))) + .withChild(createAugmentedLeafNode()) + .build(); + } + + protected static LeafNode createAugmentedLeafNode() { + return leafNode(AUGMENT_QNAME, "Augmented leaf value"); + } + + protected static ContainerNode createContainerFromAnotherNamespace() { + return ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new NodeIdentifier(ANOTHER_QNAME)) + .withChild(mapNodeBuilder(LIST_ANOTHER_NAMESPACE_QNAME) + .withChild(mapEntry(LIST_ANOTHER_NAMESPACE_QNAME, + LEAF_ANOTHER_NAMESPACE_QNAME, + "Leaf from another namespace value")) + .build()) + .build(); + } + + protected static LeafNode createLeafNode() { + return Builders.leafBuilder() + .withNodeIdentifier(NodeIdentifier.create(LEAF_QNAME)) + .withValue("Leaf value") + .build(); + } + + protected static LeafSetNode createLeafSetNode() { + final String value = "Leaf set value"; + final LeafSetEntryNode leafSetValue = Builders.leafSetEntryBuilder() + .withNodeIdentifier(new NodeWithValue<>(LEAF_SET_QNAME, value)) + .withValue(value) + .build(); + return Builders.leafSetBuilder() + .withNodeIdentifier(NodeIdentifier.create(LEAF_SET_QNAME)) + .withValue(List.of(leafSetValue)) + .build(); + } + + protected static UserLeafSetNode createUserLeafSetNode() { + final String value = "User leaf set value"; + final LeafSetEntryNode leafSetValue = Builders.leafSetEntryBuilder() + .withNodeIdentifier(new NodeWithValue<>(USER_LEAF_SET_QNAME, value)) + .withValue(value) + .build(); + return Builders.orderedLeafSetBuilder() + .withNodeIdentifier(NodeIdentifier.create(USER_LEAF_SET_QNAME)) + .withValue(List.of(leafSetValue)) + .build(); + } + + protected static UserMapNode createUserMapNode() { + return Builders.orderedMapBuilder() + .withNodeIdentifier(NodeIdentifier.create(USER_MAP_QNAME)) + .withValue(List.of(createUserMapEntryNode())) + .build(); + } + + protected static MapEntryNode createUserMapEntryNode() { + return mapEntry(USER_MAP_QNAME, USER_MAP_ENTRY_QNAME, "User map entry value"); + } + + protected static UnkeyedListNode createUnkeyedListNode() { + return Builders.unkeyedListBuilder() + .withNodeIdentifier(NodeIdentifier.create(UNKEYED_LIST_QNAME)) + .withChild(createUnkeyedListEntryNode()) + .build(); + } + + protected static UnkeyedListEntryNode createUnkeyedListEntryNode() { + return Builders.unkeyedListEntryBuilder() + .withNodeIdentifier(NodeIdentifier.create(UNKEYED_LIST_ENTRY_QNAME)) + .withChild(leafNode(UNKEYED_LIST_LEAF_QNAME, "Unkeyed list leaf value")) + .build(); + } + + protected static AnydataNode createAnyDataNode() { + return Builders.anydataBuilder(String.class) + .withNodeIdentifier(NodeIdentifier.create(ANY_DATA_QNAME)) + .withValue("Any data value") + .build(); + } +} diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodePrettyTreeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodePrettyTreeTest.java new file mode 100644 index 0000000000..4223ce148a --- /dev/null +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodePrettyTreeTest.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2021 PANTHEON.tech s.r.o. 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.impl.schema.tree; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class NormalizedNodePrettyTreeTest extends AbstractPrettyTreeTest { + @Test + public void testMapNodePrettyTree() { + assertEquals(String.join("\n", + "systemMapNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)list-a = {", + " mapEntryNode list-a = {", + " leafNode leaf-a = \"bar\"", + " systemMapNode list-b = {", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"two\"", + " }", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"one\"", + " }", + " }", + " }", + " mapEntryNode list-a = {", + " leafNode leaf-a = \"foo\"", + " }", + "}"), createMapNode().prettyTree().get()); + } + + @Test + public void testMapEntryPrettyTree() { + assertEquals(String.join("\n", + "mapEntryNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)list-a = {", + " leafNode leaf-a = \"bar\"", + " systemMapNode list-b = {", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"two\"", + " }", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"one\"", + " }", + " }", + "}"), createMapEntryNode().prettyTree().get()); + } + + @Test + public void testChoicePrettyTree() { + assertEquals(String.join("\n", + "choiceNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)choice = {", + " augmentationNode = {", + " leafNode augment = \"Augmented leaf value\"", + " }", + "}"), createChoiceNode().prettyTree().get()); + } + + @Test + public void testAugmentationPrettyTree() { + assertEquals(String.join("\n", + "augmentationNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13) = {", + " leafNode augment = \"Augmented leaf value\"", + "}"), createAugmentationNode().prettyTree().get()); + } + + @Test + public void testLeafPrettyTree() { + assertEquals("leafNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)leaf = \"Leaf value\"", + createLeafNode().prettyTree().get()); + } + + @Test + public void testLeafSetPrettyTree() { + assertEquals(String.join("\n", + "systemLeafSetNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)leaf-set = {", + " leafSetEntryNode leaf-set = \"Leaf set value\"", + "}"), createLeafSetNode().prettyTree().get()); + } + + @Test + public void testUserLeafSetPrettyTree() { + assertEquals(String.join("\n", + "userLeafSetNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)user-leaf-set = {", + " leafSetEntryNode user-leaf-set = \"User leaf set value\"", + "}"), createUserLeafSetNode().prettyTree().get()); + } + + @Test + public void testUserMapPrettyTree() { + assertEquals(String.join("\n", + "userMapNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)user-map = {", + " mapEntryNode user-map = {", + " leafNode user-map-entry = \"User map entry value\"", + " }", + "}"), createUserMapNode().prettyTree().get()); + } + + @Test + public void testUserMapEntryPrettyTree() { + assertEquals(String.join("\n", + "mapEntryNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)user-map = {", + " leafNode user-map-entry = \"User map entry value\"", + "}"), createUserMapEntryNode().prettyTree().get()); + } + + @Test + public void testUnkeyedListPrettyTree() { + assertEquals(String.join("\n", + "unkeyedListNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)unkeyed-list = {", + " unkeyedListEntryNode unkeyed-list-entry = {", + " leafNode unkeyed-list-leaf = \"Unkeyed list leaf value\"", + " }", + "}"), createUnkeyedListNode().prettyTree().get()); + } + + @Test + public void testUnkeyedListEntryPrettyTree() { + assertEquals(String.join("\n", + "unkeyedListEntryNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)unkeyed-list-entry = {", + " leafNode unkeyed-list-leaf = \"Unkeyed list leaf value\"", + "}"), createUnkeyedListEntryNode().prettyTree().get()); + } + + @Test + public void testAnyDataPrettyTree() { + assertEquals(String.join("\n", + "anydataNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)any-data = (java.lang.String)"), + createAnyDataNode().prettyTree().get()); + } + + @Test + public void testContainerPrettyTree() { + assertEquals(String.join("\n", + "containerNode (urn:opendaylight:controller:sal:dom:store:test@2014-03-13)root = {", + " userMapNode user-map = {", + " mapEntryNode user-map = {", + " leafNode user-map-entry = \"User map entry value\"", + " }", + " }", + " userLeafSetNode user-leaf-set = {", + " leafSetEntryNode user-leaf-set = \"User leaf set value\"", + " }", + " systemMapNode list-a = {", + " mapEntryNode list-a = {", + " leafNode leaf-a = \"bar\"", + " systemMapNode list-b = {", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"two\"", + " }", + " mapEntryNode list-b = {", + " leafNode leaf-b = \"one\"", + " }", + " }", + " }", + " mapEntryNode list-a = {", + " leafNode leaf-a = \"foo\"", + " }", + " }", + " containerNode (urn:opendaylight:controller:sal:dom:store:another)another = {", + " systemMapNode list-from-another-namespace = {", + " mapEntryNode list-from-another-namespace = {", + " leafNode leaf-from-another-namespace = \"Leaf from another namespace value\"", + " }", + " }", + " }", + " choiceNode choice = {", + " augmentationNode = {", + " leafNode augment = \"Augmented leaf value\"", + " }", + " }", + " anydataNode any-data = (java.lang.String)", + " unkeyedListNode unkeyed-list = {", + " unkeyedListEntryNode unkeyed-list-entry = {", + " leafNode unkeyed-list-leaf = \"Unkeyed list leaf value\"", + " }", + " }", + " leafNode leaf = \"Leaf value\"", + " systemLeafSetNode leaf-set = {", + " leafSetEntryNode leaf-set = \"Leaf set value\"", + " }", + "}"), createContainerNode().prettyTree().get()); + } +} diff --git a/yang/yang-data-impl/src/test/resources/pretty-print/another.yang b/yang/yang-data-impl/src/test/resources/pretty-print/another.yang new file mode 100644 index 0000000000..08219ae07a --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/pretty-print/another.yang @@ -0,0 +1,18 @@ +module another { + yang-version 1.1; + namespace "urn:opendaylight:controller:sal:dom:store:another"; + prefix another; + + import test { prefix test; } + + augment "/test:root" { + container another { + list list-from-another-namespace { + key "leaf-from-another-namespace"; + leaf leaf-from-another-namespace { + type string; + } + } + } + } +} diff --git a/yang/yang-data-impl/src/test/resources/pretty-print/test.yang b/yang/yang-data-impl/src/test/resources/pretty-print/test.yang new file mode 100644 index 0000000000..1c4eda9fe8 --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/pretty-print/test.yang @@ -0,0 +1,60 @@ +module test { + yang-version 1.1; + namespace "urn:opendaylight:controller:sal:dom:store:test"; + prefix test; + + revision 2014-03-13; + + container root { + list list-a { + key "leaf-a"; + leaf leaf-a { + type string; + } + list list-b { + key "leaf-b"; + leaf leaf-b { + type string; + } + } + } + + choice choice { + } + + leaf leaf { + type string; + } + + leaf-list leaf-set { + type string; + } + + leaf-list user-leaf-set { + ordered-by user; + type string; + } + + list user-map { + ordered-by user; + key "user-map-entry"; + leaf user-map-entry { + type string; + } + } + + list unkeyed-list { + leaf unkeyed-list-leaf { + type string; + } + } + + anydata any-data {} + } + + augment "/test:root/test:choice" { + leaf augment { + type string; + } + } +}