Bug 8745: Migrate EditConfig to the new XML parser 14/60014/7
authorIgor Foltin <igor.foltin@pantheon.tech>
Thu, 6 Jul 2017 10:50:09 +0000 (12:50 +0200)
committerIgor Foltin <igor.foltin@pantheon.tech>
Tue, 18 Jul 2017 06:15:30 +0000 (06:15 +0000)
Switch EditConfig class to use the new XML parser from YANG tools.

Introduce EditOperationNormalizedNodeStreamWriter as a replacement
of the old XML parser extension EditOperationStrategyProvider which
has been used for tracking edit-config data tree operations defined
in the parsed XML source.

Remove EditOperationStrategyProvider as it is not needed anymore.

Change-Id: I737b134805120368a352568fde3f19517a9871df
Signed-off-by: Igor Foltin <igor.foltin@pantheon.tech>
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditConfig.java
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationNormalizedNodeStreamWriter.java [new file with mode: 0644]
netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationStrategyProvider.java [deleted file]

index d1998b2b12e33689847d4a7270c2cd67e96d9293..12e34b5c803333e46bc05dbbc44d44f41dfb84fa 100644 (file)
@@ -11,9 +11,9 @@ package org.opendaylight.netconf.mdsal.connector.ops;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
+import java.io.StringReader;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.util.Collections;
 import java.util.List;
 import java.util.ListIterator;
 import java.util.Map;
@@ -33,6 +33,7 @@ import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
 import org.opendaylight.netconf.mdsal.connector.TransactionProvider;
 import org.opendaylight.netconf.mdsal.connector.ops.DataTreeChangeTracker.DataTreeChange;
 import org.opendaylight.netconf.util.mapping.AbstractSingletonNetconfOperation;
+import org.opendaylight.yangtools.util.xml.UntrustedXML;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.ModifyAction;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -40,9 +41,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
 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.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.xml.XmlParserStream;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
@@ -93,10 +95,8 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
             final DataSchemaNode schemaNode = getSchemaNodeFromNamespace(ns, element).get();
 
             final DataTreeChangeTracker changeTracker = new DataTreeChangeTracker(defaultAction);
-            final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider =
-                    new EditOperationStrategyProvider(changeTracker);
 
-            parseIntoNormalizedNode(schemaNode, element, editOperationStrategyProvider);
+            parseIntoNormalizedNode(schemaNode, element, changeTracker);
             executeOperations(changeTracker);
         }
 
@@ -206,23 +206,27 @@ public class EditConfig extends AbstractSingletonNetconfOperation {
         }
     }
 
+    @SuppressWarnings("checkstyle:IllegalCatch")
     private NormalizedNode<?, ?> parseIntoNormalizedNode(final DataSchemaNode schemaNode, final XmlElement element,
-            final DomToNormalizedNodeParserFactory.BuildingStrategyProvider editOperationStrategyProvider) {
-        if (schemaNode instanceof ContainerSchemaNode) {
-            return DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider(),
-                            schemaContext.getCurrentContext(), editOperationStrategyProvider)
-                    .getContainerNodeParser()
-                    .parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
-        } else if (schemaNode instanceof ListSchemaNode) {
-            return DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider(),
-                            schemaContext.getCurrentContext(), editOperationStrategyProvider)
-                    .getMapNodeParser()
-                    .parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
-        } else {
+            final DataTreeChangeTracker changeTracker) throws DocumentedException {
+        if (!(schemaNode instanceof ContainerSchemaNode) && !(schemaNode instanceof ListSchemaNode)) {
             //this should never happen since edit-config on any other node type should not be possible nor makes sense
             LOG.debug("DataNode from module is not ContainerSchemaNode nor ListSchemaNode, aborting..");
+            throw new UnsupportedOperationException("implement exception if parse fails");
         }
-        throw new UnsupportedOperationException("implement exception if parse fails");
+
+        final NormalizedNodeResult resultHolder = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter writer = new EditOperationNormalizedNodeStreamWriter(resultHolder,
+                changeTracker);
+        final XmlParserStream xmlParser = XmlParserStream.create(writer, schemaContext.getCurrentContext(), schemaNode);
+        try {
+            xmlParser.parse(UntrustedXML.createXMLStreamReader(new StringReader(XmlUtil.toString(element))));
+        } catch (final Exception ex) {
+            throw new NetconfDocumentedException("Error parsing input: " + ex.getMessage(), ex, ErrorType.PROTOCOL,
+                    ErrorTag.MALFORMED_MESSAGE, ErrorSeverity.ERROR);
+        }
+
+        return resultHolder.getResult();
     }
 
     private Optional<DataSchemaNode> getSchemaNodeFromNamespace(final String namespace, final XmlElement element)
diff --git a/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationNormalizedNodeStreamWriter.java b/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationNormalizedNodeStreamWriter.java
new file mode 100644 (file)
index 0000000..7a98342
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies 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.netconf.mdsal.connector.ops;
+
+import java.util.ArrayList;
+import java.util.Map;
+import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
+import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfigInput;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+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.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamAttributeWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+
+final class EditOperationNormalizedNodeStreamWriter extends ImmutableNormalizedNodeStreamWriter
+        implements NormalizedNodeStreamAttributeWriter {
+    private static final QName OPERATION_ATTRIBUTE = QName.create(EditConfigInput.QNAME.getNamespace(), null,
+            XmlNetconfConstants.OPERATION_ATTR_KEY);
+
+    private final DataTreeChangeTracker dataTreeChangeTracker;
+
+    EditOperationNormalizedNodeStreamWriter(final NormalizedNodeResult result,
+            final DataTreeChangeTracker dataTreeChangeTracker) {
+        super(result);
+        this.dataTreeChangeTracker = dataTreeChangeTracker;
+    }
+
+    @Override
+    public void leafNode(final NodeIdentifier name, final Object value, final Map<QName, String> attributes) {
+        super.leafNode(name, value);
+
+        final String operation = attributes.get(OPERATION_ATTRIBUTE);
+        if (operation == null) {
+            return;
+        }
+
+        final ModifyAction action = ModifyAction.fromXmlValue(operation);
+        if (dataTreeChangeTracker.getDeleteOperationTracker() == 0
+                && dataTreeChangeTracker.getRemoveOperationTracker() == 0) {
+            if (!action.equals(dataTreeChangeTracker.peekAction())) {
+                final LeafNode<Object> node = ImmutableNodes.leafNode(name, value);
+                dataTreeChangeTracker.pushPath(name);
+                dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action,
+                        new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+                getCurrent().removeChild(dataTreeChangeTracker.popPath());
+            }
+        }
+    }
+
+    @Override
+    public void startLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        super.startLeafSet(name, childSizeHint);
+        dataTreeChangeTracker.pushPath(name);
+    }
+
+    @Override
+    public void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) {
+        super.startOrderedLeafSet(name, childSizeHint);
+        dataTreeChangeTracker.pushPath(name);
+    }
+
+    @Override
+    public void leafSetEntryNode(final QName name, final Object value, final Map<QName, String> attributes) {
+        super.leafSetEntryNode(name, value);
+        final String operation = attributes.get(OPERATION_ATTRIBUTE);
+        if (operation == null) {
+            return;
+        }
+
+        ModifyAction action = ModifyAction.fromXmlValue(operation);
+        if (dataTreeChangeTracker.getDeleteOperationTracker() == 0
+                && dataTreeChangeTracker.getRemoveOperationTracker() == 0) {
+            if (!action.equals(dataTreeChangeTracker.peekAction())) {
+                final LeafSetEntryNode<?> node = Builders.leafSetEntryBuilder().withNodeIdentifier(
+                        new NodeWithValue(name, value)).withValue(value).build();
+                dataTreeChangeTracker.pushPath(node.getIdentifier());
+                dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action,
+                        new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+                getCurrent().removeChild(dataTreeChangeTracker.popPath());
+            }
+        }
+    }
+
+    @Override
+    public void startContainerNode(final NodeIdentifier name, final int childSizeHint,
+            final Map<QName, String> attributes) {
+        super.startContainerNode(name, childSizeHint);
+        trackDataContainerNode(name, attributes);
+    }
+
+    @Override
+    public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint,
+            final Map<QName, String> attributes) {
+        super.startYangModeledAnyXmlNode(name, childSizeHint);
+        trackDataContainerNode(name, attributes);
+    }
+
+    @Override
+    public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) {
+        super.startUnkeyedList(name, childSizeHint);
+        dataTreeChangeTracker.pushPath(name);
+    }
+
+    @Override
+    public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint,
+            final Map<QName, String> attributes) {
+        super.startUnkeyedListItem(name, childSizeHint);
+        trackDataContainerNode(name, attributes);
+    }
+
+    @Override
+    public void startMapNode(final NodeIdentifier name, final int childSizeHint) {
+        super.startMapNode(name, childSizeHint);
+        dataTreeChangeTracker.pushPath(name);
+    }
+
+    @Override
+    public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) {
+        super.startOrderedMapNode(name, childSizeHint);
+        dataTreeChangeTracker.pushPath(name);
+    }
+
+    @Override
+    public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint,
+            final Map<QName, String> attributes)  {
+        super.startMapEntryNode(identifier, childSizeHint);
+        trackDataContainerNode(identifier, attributes);
+    }
+
+    @Override
+    public void startAugmentationNode(final AugmentationIdentifier identifier) {
+        super.startAugmentationNode(identifier);
+        trackMixinNode(identifier);
+    }
+
+    @Override
+    public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) {
+        super.startChoiceNode(name, childSizeHint);
+        trackMixinNode(name);
+    }
+
+    // for augments and choices
+    private void trackMixinNode(final PathArgument identifier) {
+        dataTreeChangeTracker.pushPath(identifier);
+        dataTreeChangeTracker.pushAction(dataTreeChangeTracker.peekAction() != null
+                ? dataTreeChangeTracker.peekAction() : dataTreeChangeTracker.getDefaultAction());
+    }
+
+    // for containers, (unkeyed) list entries and yang-modeled-anyxmls
+    private void trackDataContainerNode(final PathArgument identifier, final Map<QName, String> attributes) {
+        dataTreeChangeTracker.pushPath(identifier);
+        final String operation = attributes.get(OPERATION_ATTRIBUTE);
+        if (operation != null) {
+            dataTreeChangeTracker.pushAction(ModifyAction.fromXmlValue(operation));
+        } else {
+            dataTreeChangeTracker.pushAction(dataTreeChangeTracker.peekAction() != null
+                    ? dataTreeChangeTracker.peekAction() : dataTreeChangeTracker.getDefaultAction());
+        }
+    }
+
+    @Override
+    @SuppressWarnings({"rawtypes","unchecked"})
+    public void endNode() {
+        final NormalizedNodeContainerBuilder finishedBuilder = getBuilders().peek();
+        final NormalizedNode<PathArgument, ?> product = finishedBuilder.build();
+        super.endNode();
+
+        // for augments, choices, containers, (unkeyed) list entries and yang-modeled-anyxmls
+        if (finishedBuilder instanceof DataContainerNodeBuilder) {
+            final ModifyAction currentAction = dataTreeChangeTracker.popAction();
+
+            //if we know that we are going to delete a parent node just complete the entire subtree
+            if (dataTreeChangeTracker.getDeleteOperationTracker() > 0
+                    || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
+                dataTreeChangeTracker.popPath();
+            } else {
+                //if parent and current actions don't match create a DataTreeChange and add it to the change list
+                //don't add a new child to the parent node
+                if (!currentAction.equals(dataTreeChangeTracker.peekAction())) {
+                    dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(product,
+                            currentAction, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
+                    if (getCurrent() instanceof NormalizedNodeResultBuilder) {
+                        dataTreeChangeTracker.popPath();
+                        return;
+                    }
+                    getCurrent().removeChild(dataTreeChangeTracker.popPath());
+                } else {
+                    dataTreeChangeTracker.popPath();
+                    return;
+                }
+            }
+        }
+
+        // for (ordered) leaf-lists, (ordered) lists and unkeyed lists
+        if (finishedBuilder instanceof CollectionNodeBuilder) {
+            dataTreeChangeTracker.popPath();
+        }
+    }
+}
diff --git a/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationStrategyProvider.java b/netconf/mdsal-netconf-connector/src/main/java/org/opendaylight/netconf/mdsal/connector/ops/EditOperationStrategyProvider.java
deleted file mode 100644 (file)
index a29e37d..0000000
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
- * 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.netconf.mdsal.connector.ops;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Map;
-import javax.annotation.Nullable;
-import org.opendaylight.netconf.api.xml.XmlNetconfConstants;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.netconf.base._1._0.rev110601.EditConfigInput;
-import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.data.api.ModifyAction;
-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.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.DataContainerNode;
-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.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.OrderedMapNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
-import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.parser.ExtensibleParser;
-import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
-
-class EditOperationStrategyProvider extends DomToNormalizedNodeParserFactory.BuildingStrategyProvider {
-
-    private static final QName OPERATION_ATTRIBUTE = QName.create(EditConfigInput.QNAME.getNamespace(), null,
-            XmlNetconfConstants.OPERATION_ATTR_KEY);
-
-    private final DataTreeChangeTracker changeTracker;
-
-    EditOperationStrategyProvider(final DataTreeChangeTracker changeTracker) {
-        this.changeTracker = changeTracker;
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, LeafNode<?>> forLeaf() {
-        return new NetconfOperationLeafStrategy(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, ContainerNode> forContainer() {
-        return new NetconfOperationContainerStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, MapNode> forMap() {
-        return new NetconfOperationCollectionStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeWithValue, LeafSetEntryNode<?>> forLeafSetEntry() {
-        return new NetconfOperationLeafSetEntryStrategy(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifierWithPredicates, MapEntryNode> forMapEntry() {
-        return new NetconfOperationContainerStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, OrderedMapNode> forOrderedList() {
-        return new NetconfOperationCollectionStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, UnkeyedListEntryNode> forUnkeyedListEntry() {
-        return new NetconfOperationContainerStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, UnkeyedListNode> forUnkeyedList() {
-        return new NetconfOperationCollectionStrategy<>(changeTracker);
-    }
-
-    @Override
-    protected ExtensibleParser.BuildingStrategy<NodeIdentifier, ChoiceNode> forChoice() {
-        return new NetconfOperationContainerStrategy<>(changeTracker);
-    }
-
-    @Override
-    public ExtensibleParser.BuildingStrategy<AugmentationIdentifier, AugmentationNode> forAugmentation() {
-        return new NetconfOperationContainerStrategy<>(changeTracker);
-    }
-
-    private static class NetconfOperationCollectionStrategy<N extends NormalizedNode<NodeIdentifier, ?>>
-            implements ExtensibleParser.BuildingStrategy<NodeIdentifier, N> {
-        private final DataTreeChangeTracker changeTracker;
-
-        NetconfOperationCollectionStrategy(final DataTreeChangeTracker changeTracker) {
-            this.changeTracker = changeTracker;
-        }
-
-        @Nullable
-        @Override
-        public N build(final NormalizedNodeBuilder<NodeIdentifier, ?, N> builder) {
-            changeTracker.popPath();
-            return builder.build();
-        }
-
-        @Override
-        public void prepareAttributes(
-                final Map<QName, String> attributes,
-                final NormalizedNodeBuilder<NodeIdentifier, ?, N> containerBuilder) {
-            changeTracker.pushPath(containerBuilder.build().getIdentifier());
-        }
-    }
-
-    public static final class NetconfOperationLeafStrategy
-            implements ExtensibleParser.BuildingStrategy<NodeIdentifier, LeafNode<?>> {
-
-        private final DataTreeChangeTracker dataTreeChangeTracker;
-
-        NetconfOperationLeafStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
-            this.dataTreeChangeTracker = dataTreeChangeTracker;
-        }
-
-        @Nullable
-        @Override
-        public LeafNode<?> build(
-                final NormalizedNodeBuilder<NodeIdentifier, ?, LeafNode<?>> builder) {
-            LeafNode<?> node = builder.build();
-            String operation = (String) node.getAttributeValue(OPERATION_ATTRIBUTE);
-            if (operation == null) {
-                return node;
-            }
-
-            if (builder instanceof AttributesBuilder<?>) {
-                ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
-            }
-
-            node = builder.build();
-
-            ModifyAction action = ModifyAction.fromXmlValue(operation);
-            if (dataTreeChangeTracker.getDeleteOperationTracker() > 0
-                    || dataTreeChangeTracker .getRemoveOperationTracker() > 0) {
-                return node;
-            }
-
-            if (action.equals(dataTreeChangeTracker.peekAction())) {
-                return node;
-            }
-
-            dataTreeChangeTracker.pushPath(node.getIdentifier());
-            dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action,
-                new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
-            dataTreeChangeTracker.popPath();
-            return null;
-        }
-
-        @Override
-        public void prepareAttributes(
-                final Map<QName, String> attributes,
-                final NormalizedNodeBuilder<NodeIdentifier, ?, LeafNode<?>> containerBuilder) {
-            // Noop
-        }
-    }
-
-    public static final class NetconfOperationLeafSetEntryStrategy
-            implements ExtensibleParser.BuildingStrategy<NodeWithValue, LeafSetEntryNode<?>> {
-
-        private final DataTreeChangeTracker dataTreeChangeTracker;
-
-        NetconfOperationLeafSetEntryStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
-            this.dataTreeChangeTracker = dataTreeChangeTracker;
-        }
-
-        @Nullable
-        @Override
-        public LeafSetEntryNode<?> build(final NormalizedNodeBuilder<NodeWithValue, ?, LeafSetEntryNode<?>> builder) {
-            LeafSetEntryNode<?> node = builder.build();
-            String operation = (String) node.getAttributeValue(OPERATION_ATTRIBUTE);
-            if (operation == null) {
-                return node;
-            }
-
-            if (builder instanceof AttributesBuilder<?>) {
-                ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
-            }
-
-            node = builder.build();
-
-            ModifyAction action = ModifyAction.fromXmlValue(operation);
-            if (dataTreeChangeTracker.getDeleteOperationTracker() > 0
-                    || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
-                return node;
-            }
-
-            if (action.equals(dataTreeChangeTracker.peekAction())) {
-                return node;
-            }
-
-            dataTreeChangeTracker.pushPath(node.getIdentifier());
-            dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node, action,
-                new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
-            dataTreeChangeTracker.popPath();
-            return null;
-        }
-
-        @Override
-        public void prepareAttributes(
-                final Map<QName, String> attributes,
-                final NormalizedNodeBuilder<NodeWithValue, ?, LeafSetEntryNode<?>> containerBuilder) {
-        }
-    }
-
-    public static final class NetconfOperationContainerStrategy<P extends PathArgument, N
-            extends DataContainerNode<P>> implements ExtensibleParser.BuildingStrategy<P, N> {
-
-        private final DataTreeChangeTracker dataTreeChangeTracker;
-
-        NetconfOperationContainerStrategy(final DataTreeChangeTracker dataTreeChangeTracker) {
-            this.dataTreeChangeTracker = dataTreeChangeTracker;
-        }
-
-        @Nullable
-        @Override
-        public N build(final NormalizedNodeBuilder<P, ?, N> builder) {
-            if (builder instanceof AttributesBuilder<?>) {
-                ((AttributesBuilder<?>) builder).withAttributes(Collections.<QName, String>emptyMap());
-            }
-
-            final N node = builder.build();
-            final ModifyAction currentAction = dataTreeChangeTracker.popAction();
-
-            //if we know that we are going to delete a parent node just complete the entire subtree
-            if (dataTreeChangeTracker.getDeleteOperationTracker() > 0
-                    || dataTreeChangeTracker.getRemoveOperationTracker() > 0) {
-                dataTreeChangeTracker.popPath();
-                return node;
-            }
-
-            if (currentAction.equals(dataTreeChangeTracker.peekAction())) {
-                dataTreeChangeTracker.popPath();
-                return node;
-            }
-
-            //if parent and current actions dont match create a DataTreeChange and add it to the change list
-            //dont add a new child to the parent node
-            dataTreeChangeTracker.addDataTreeChange(new DataTreeChangeTracker.DataTreeChange(node,
-                currentAction, new ArrayList<>(dataTreeChangeTracker.getCurrentPath())));
-            dataTreeChangeTracker.popPath();
-            return null;
-        }
-
-        @Override
-        public void prepareAttributes(final Map<QName, String> attributes,
-                                      final NormalizedNodeBuilder<P, ?, N> containerBuilder) {
-            dataTreeChangeTracker.pushPath(containerBuilder.build().getIdentifier());
-            final String operation = attributes.get(OPERATION_ATTRIBUTE);
-            if (operation != null) {
-                dataTreeChangeTracker.pushAction(ModifyAction.fromXmlValue(operation));
-            } else {
-                dataTreeChangeTracker.pushAction(dataTreeChangeTracker.peekAction() != null
-                        ? dataTreeChangeTracker.peekAction() : dataTreeChangeTracker.getDefaultAction());
-            }
-        }
-    }
-}