From 7bef3c9e3f411d3e4abdb5ff43a618537cc9c936 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Thu, 28 Mar 2019 12:46:34 +0100 Subject: [PATCH] Add ReusableNormalizedNodePruner This version of NormalizedNodePruner is reusable across invocations, and improving CPU and memory efficiency in bulk operations due to not needing to allocate stack nor lookup DataSchemaContextTree. JIRA: CONTROLLER-1887 Change-Id: I1798f5f6255cd23702bf3792c1f4f5149f92d208 Signed-off-by: Robert Varga --- .../AbstractNormalizedNodePruner.java | 285 ++++++++++++++++++ .../transformer/NormalizedNodePruner.java | 229 +------------- .../ReusableNormalizedNodePruner.java | 77 +++++ 3 files changed, 368 insertions(+), 223 deletions(-) create mode 100644 opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java create mode 100644 opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java new file mode 100644 index 0000000000..9ce039cd1a --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2015 Cisco Systems, Inc. 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.controller.cluster.datastore.node.utils.transformer; + +import static com.google.common.base.Preconditions.checkState; +import static java.util.Objects.requireNonNull; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.NoSuchElementException; +import java.util.Optional; +import javax.xml.transform.dom.DOMSource; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +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.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.Builders; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding + * schema element in the passed in SchemaContext. + */ +abstract class AbstractNormalizedNodePruner implements NormalizedNodeStreamWriter { + enum State { + UNITIALIZED, + OPEN, + CLOSED; + } + + private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodePruner.class); + + private final Deque stack = new ArrayDeque<>(); + private final DataSchemaContextTree tree; + + private DataSchemaContextNode nodePathSchemaNode; + private State state = State.UNITIALIZED; + + // FIXME: package-private to support unguarded NormalizedNodePruner access + NormalizedNode normalizedNode; + + AbstractNormalizedNodePruner(final DataSchemaContextTree tree) { + this.tree = requireNonNull(tree); + } + + AbstractNormalizedNodePruner(final SchemaContext schemaContext) { + this(DataSchemaContextTree.from(schemaContext)); + } + + final DataSchemaContextTree getTree() { + return tree; + } + + final void initialize(final YangInstanceIdentifier nodePath) { + nodePathSchemaNode = tree.findChild(nodePath).orElse(null); + normalizedNode = null; + stack.clear(); + state = State.OPEN; + } + + @SuppressWarnings("unchecked") + @Override + public void leafNode(final NodeIdentifier nodeIdentifier, final Object value) { + checkNotSealed(); + + NormalizedNodeBuilderWrapper parent = stack.peek(); + LeafNode leafNode = Builders.leafBuilder().withNodeIdentifier(nodeIdentifier).withValue(value).build(); + if (parent != null) { + if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) { + parent.builder().addChild(leafNode); + } + } else { + // If there's no parent node then this is a stand alone LeafNode. + if (nodePathSchemaNode != null) { + this.normalizedNode = leafNode; + } + + state = State.CLOSED; + } + } + + @Override + public void startLeafSet(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startOrderedLeafSet(final NodeIdentifier nodeIdentifier, final int str) { + addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @SuppressWarnings("unchecked") + @Override + public void leafSetEntryNode(final QName name, final Object value) { + checkNotSealed(); + + NormalizedNodeBuilderWrapper parent = stack.peek(); + if (parent != null) { + if (hasValidSchema(name, parent)) { + parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(value) + .withNodeIdentifier(new NodeWithValue<>(parent.nodeType(), value)) + .build()); + } + } else { + // If there's no parent LeafSetNode then this is a stand alone + // LeafSetEntryNode. + if (nodePathSchemaNode != null) { + this.normalizedNode = Builders.leafSetEntryBuilder().withValue(value).withNodeIdentifier( + new NodeWithValue<>(name, value)).build(); + } + + state = State.CLOSED; + } + } + + @Override + public void startContainerNode(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + @Override + public void startUnkeyedList(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startUnkeyedListItem(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startMapNode(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifierWithPredicates, final int count) { + addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates), + nodeIdentifierWithPredicates); + } + + @Override + public void startOrderedMapNode(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startChoiceNode(final NodeIdentifier nodeIdentifier, final int count) { + addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); + } + + @Override + public void startAugmentationNode(final AugmentationIdentifier augmentationIdentifier) { + addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier); + } + + @SuppressWarnings("unchecked") + @Override + public void anyxmlNode(final NodeIdentifier nodeIdentifier, final Object value) { + checkNotSealed(); + + NormalizedNodeBuilderWrapper parent = stack.peek(); + AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) value) + .build(); + if (parent != null) { + if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) { + parent.builder().addChild(anyXmlNode); + } + } else { + // If there's no parent node then this is a stand alone AnyXmlNode. + if (nodePathSchemaNode != null) { + this.normalizedNode = anyXmlNode; + } + + state = State.CLOSED; + } + } + + @SuppressWarnings("unchecked") + @Override + public void endNode() { + checkNotSealed(); + + final NormalizedNodeBuilderWrapper child; + try { + child = stack.pop(); + } catch (NoSuchElementException e) { + throw new IllegalStateException("endNode called on an empty stack", e); + } + + if (child.getSchema() == null) { + LOG.debug("Schema not found for {}", child.identifier()); + if (stack.isEmpty()) { + normalizedNode = null; + state = State.CLOSED; + } + return; + } + + final NormalizedNode newNode = child.builder().build(); + final NormalizedNodeBuilderWrapper parent = stack.peek(); + if (parent == null) { + normalizedNode = newNode; + state = State.CLOSED; + } else { + parent.builder().addChild(newNode); + } + } + + @Override + public void close() { + state = State.CLOSED; + stack.clear(); + } + + @Override + public void flush() { + // No-op + } + + /** + * Return the resulting normalized node. + * + * @return Resulting node for the path, if it was not pruned + * @throws IllegalStateException if this pruner has not been closed + */ + public final Optional> getResult() { + checkState(state == State.CLOSED, "Cannot get result in state %s", state); + return Optional.ofNullable(normalizedNode); + } + + private void checkNotSealed() { + checkState(state == State.OPEN, "Illegal operation in state %s", state); + } + + private static boolean hasValidSchema(final QName name, final NormalizedNodeBuilderWrapper parent) { + final DataSchemaContextNode parentSchema = parent.getSchema(); + final boolean valid = parentSchema != null && parentSchema.getChild(name) != null; + if (!valid) { + LOG.debug("Schema not found for {}", name); + } + + return valid; + } + + private NormalizedNodeBuilderWrapper addBuilder(final NormalizedNodeContainerBuilder builder, + final PathArgument identifier) { + checkNotSealed(); + + final DataSchemaContextNode schemaNode; + final NormalizedNodeBuilderWrapper parent = stack.peek(); + if (parent != null) { + final DataSchemaContextNode parentSchema = parent.getSchema(); + schemaNode = parentSchema == null ? null : parentSchema.getChild(identifier); + } else { + schemaNode = nodePathSchemaNode; + } + + NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode); + stack.push(wrapper); + return wrapper; + } +} diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java index 2082654543..4e3bb5e82f 100644 --- a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java @@ -7,244 +7,27 @@ */ package org.opendaylight.controller.cluster.datastore.node.utils.transformer; -import static com.google.common.base.Preconditions.checkState; - import java.net.URI; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.NoSuchElementException; -import javax.xml.transform.dom.DOMSource; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -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.NodeIdentifierWithPredicates; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; -import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder; -import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode; -import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding * schema element in the passed in SchemaContext. + * + * @deprecated Use {@link AbstractNormalizedNodePruner} instead. */ -public class NormalizedNodePruner implements NormalizedNodeStreamWriter { - private static final Logger LOG = LoggerFactory.getLogger(NormalizedNodePruner.class); - +@Deprecated +public class NormalizedNodePruner extends AbstractNormalizedNodePruner { public static final URI BASE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0"); - private final Deque stack = new ArrayDeque<>(); - private final DataSchemaContextNode nodePathSchemaNode; - - private NormalizedNode normalizedNode; - private boolean sealed = false; - public NormalizedNodePruner(final YangInstanceIdentifier nodePath, final SchemaContext schemaContext) { - nodePathSchemaNode = DataSchemaContextTree.from(schemaContext).findChild(nodePath).orElse(null); - } - - @SuppressWarnings("unchecked") - @Override - public void leafNode(final NodeIdentifier nodeIdentifier, final Object value) { - checkNotSealed(); - - NormalizedNodeBuilderWrapper parent = stack.peek(); - LeafNode leafNode = Builders.leafBuilder().withNodeIdentifier(nodeIdentifier).withValue(value).build(); - if (parent != null) { - if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) { - parent.builder().addChild(leafNode); - } - } else { - // If there's no parent node then this is a stand alone LeafNode. - if (nodePathSchemaNode != null) { - this.normalizedNode = leafNode; - } - - sealed = true; - } - } - - @Override - public void startLeafSet(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startOrderedLeafSet(final NodeIdentifier nodeIdentifier, final int str) { - addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @SuppressWarnings("unchecked") - @Override - public void leafSetEntryNode(final QName name, final Object value) { - checkNotSealed(); - - NormalizedNodeBuilderWrapper parent = stack.peek(); - if (parent != null) { - if (hasValidSchema(name, parent)) { - parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(value) - .withNodeIdentifier(new NodeWithValue<>(parent.nodeType(), value)) - .build()); - } - } else { - // If there's no parent LeafSetNode then this is a stand alone - // LeafSetEntryNode. - if (nodePathSchemaNode != null) { - this.normalizedNode = Builders.leafSetEntryBuilder().withValue(value).withNodeIdentifier( - new NodeWithValue<>(name, value)).build(); - } - - sealed = true; - } - } - - @Override - public void startContainerNode(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) { - throw new UnsupportedOperationException("Not implemented yet"); - } - - @Override - public void startUnkeyedList(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startUnkeyedListItem(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startMapNode(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifierWithPredicates, final int count) { - addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates), - nodeIdentifierWithPredicates); - } - - @Override - public void startOrderedMapNode(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startChoiceNode(final NodeIdentifier nodeIdentifier, final int count) { - addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier); - } - - @Override - public void startAugmentationNode(final AugmentationIdentifier augmentationIdentifier) { - addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier); - } - - @SuppressWarnings("unchecked") - @Override - public void anyxmlNode(final NodeIdentifier nodeIdentifier, final Object value) { - checkNotSealed(); - - NormalizedNodeBuilderWrapper parent = stack.peek(); - AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) value) - .build(); - if (parent != null) { - if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) { - parent.builder().addChild(anyXmlNode); - } - } else { - // If there's no parent node then this is a stand alone AnyXmlNode. - if (nodePathSchemaNode != null) { - this.normalizedNode = anyXmlNode; - } - - sealed = true; - } - } - - @SuppressWarnings("unchecked") - @Override - public void endNode() { - checkNotSealed(); - - final NormalizedNodeBuilderWrapper child; - try { - child = stack.pop(); - } catch (NoSuchElementException e) { - throw new IllegalStateException("endNode called on an empty stack", e); - } - - if (child.getSchema() == null) { - LOG.debug("Schema not found for {}", child.identifier()); - return; - } - - NormalizedNode newNode = child.builder().build(); - if (stack.size() > 0) { - NormalizedNodeBuilderWrapper parent = stack.peek(); - parent.builder().addChild(newNode); - } else { - this.normalizedNode = newNode; - sealed = true; - } - } - - @Override - public void close() { - sealed = true; - } - - @Override - public void flush() { - // No-op + super(schemaContext); + initialize(nodePath); } public NormalizedNode normalizedNode() { return normalizedNode; } - - private void checkNotSealed() { - checkState(!sealed, "Pruner can be used only once"); - } - - private static boolean hasValidSchema(final QName name, final NormalizedNodeBuilderWrapper parent) { - final DataSchemaContextNode parentSchema = parent.getSchema(); - final boolean valid = parentSchema != null && parentSchema.getChild(name) != null; - if (!valid) { - LOG.debug("Schema not found for {}", name); - } - - return valid; - } - - private NormalizedNodeBuilderWrapper addBuilder(final NormalizedNodeContainerBuilder builder, - final PathArgument identifier) { - checkNotSealed(); - - final DataSchemaContextNode schemaNode; - final NormalizedNodeBuilderWrapper parent = stack.peek(); - if (parent != null) { - final DataSchemaContextNode parentSchema = parent.getSchema(); - schemaNode = parentSchema == null ? null : parentSchema.getChild(identifier); - } else { - schemaNode = nodePathSchemaNode; - } - - NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode); - stack.push(wrapper); - return wrapper; - } } diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java new file mode 100644 index 0000000000..1e95766f84 --- /dev/null +++ b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java @@ -0,0 +1,77 @@ +/* + * 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.controller.cluster.datastore.node.utils.transformer; + +import com.google.common.annotations.Beta; +import org.eclipse.jdt.annotation.NonNull; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * The NormalizedNodePruner removes all nodes from the input NormalizedNode that do not have a corresponding + * schema element in the passed in SchemaContext. + * + *

+ * Unlike {@link NormalizedNodePruner}, this class can be reused multiple times and must be initialized before each use + * through {@link #initializeForPath(YangInstanceIdentifier)}. + */ +@Beta +public final class ReusableNormalizedNodePruner extends AbstractNormalizedNodePruner { + private ReusableNormalizedNodePruner(final SchemaContext schemaContext) { + super(schemaContext); + } + + private ReusableNormalizedNodePruner(final DataSchemaContextTree tree) { + super(tree); + } + + /** + * Create a new pruner bound to a SchemaContext. + * + * @param schemaContext SchemaContext to use + * @return A new uninitialized pruner + * @throws NullPointerException if {@code schemaContext} is null + */ + public static @NonNull ReusableNormalizedNodePruner forSchemaContext(final SchemaContext schemaContext) { + return new ReusableNormalizedNodePruner(schemaContext); + } + + /** + * Create a new pruner bound to a DataSchemaContextTree. This is a more efficient alternative of + * {@link #forSchemaContext(SchemaContext)}. + * + * @param tree DataSchemaContextTree to use + * @return A new uninitialized pruner + * @throws NullPointerException if {@code schemaContext} is null + */ + public static @NonNull ReusableNormalizedNodePruner forDataSchemaContext(final DataSchemaContextTree tree) { + return new ReusableNormalizedNodePruner(tree); + } + + /** + * Return a new instance, which is backed but the same DataSchemaContextTree, but does not share any state and is + * uninitialized. This is equivalent to {@link #forDataSchemaContext(DataSchemaContextTree)} and is provided for + * convenience. + * + * @return A new uninitialized pruner bound to the same SchemaContext as this one. + */ + public @NonNull ReusableNormalizedNodePruner duplicate() { + return new ReusableNormalizedNodePruner(getTree()); + } + + /** + * Initialize this pruner for processing a node at specified path. + * + * @param path Path that will be processed next + * @throws NullPointerException if {@code path} is null + */ + public void initializeForPath(final YangInstanceIdentifier path) { + initialize(path); + } +} -- 2.36.6