Add ReusableNormalizedNodePruner 57/81257/4
authorRobert Varga <robert.varga@pantheon.tech>
Thu, 28 Mar 2019 11:46:34 +0000 (12:46 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Thu, 28 Mar 2019 15:02:52 +0000 (16:02 +0100)
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 <robert.varga@pantheon.tech>
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/AbstractNormalizedNodePruner.java [new file with mode: 0644]
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/NormalizedNodePruner.java
opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/utils/transformer/ReusableNormalizedNodePruner.java [new file with mode: 0644]

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 (file)
index 0000000..9ce039c
--- /dev/null
@@ -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<NormalizedNodeBuilderWrapper> 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<Object> 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<NormalizedNode<?, ?>> 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;
+    }
+}
index 2082654543d9cd459af0d02b16e1e88466696f2f..4e3bb5e82fedf2d9a0d913548292086413b95930 100644 (file)
  */
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
  */
 package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
 
-import static com.google.common.base.Preconditions.checkState;
-
 import java.net.URI;
 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;
-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.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.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.
 
 /**
  * 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");
 
     public static final URI BASE_NAMESPACE = URI.create("urn:ietf:params:xml:ns:netconf:base:1.0");
 
-    private final Deque<NormalizedNodeBuilderWrapper> stack = new ArrayDeque<>();
-    private final DataSchemaContextNode<?> nodePathSchemaNode;
-
-    private NormalizedNode<?, ?> normalizedNode;
-    private boolean sealed = false;
-
     public NormalizedNodePruner(final YangInstanceIdentifier nodePath, final SchemaContext schemaContext) {
     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<Object> 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;
     }
     }
 
     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 (file)
index 0000000..1e95766
--- /dev/null
@@ -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.
+ *
+ * <p>
+ * 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);
+    }
+}