Created sal-clustering-commons and moved
[controller.git] / opendaylight / md-sal / sal-clustering-commons / src / main / java / org / opendaylight / controller / cluster / datastore / node / NodeToNormalizedNodeBuilder.java
diff --git a/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeToNormalizedNodeBuilder.java b/opendaylight/md-sal/sal-clustering-commons/src/main/java/org/opendaylight/controller/cluster/datastore/node/NodeToNormalizedNodeBuilder.java
new file mode 100644 (file)
index 0000000..03d632b
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ *
+ *  Copyright (c) 2014 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;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import org.opendaylight.controller.cluster.datastore.node.utils.NodeIdentifierFactory;
+import org.opendaylight.controller.protobuff.messages.common.NormalizedNodeMessages.Node;
+import org.opendaylight.yangtools.concepts.Identifiable;
+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.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.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.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.NormalizedNodeContainer;
+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.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+/**
+ * NormalizedNodeBuilder is a builder that walks through a tree like structure and constructs a
+ * NormalizedNode from it.
+ * <p/>
+ * A large part of this code has been copied over from a similar class in sal-common-impl which was
+ * originally supposed to convert a CompositeNode to NormalizedNode
+ *
+ * @param <T>
+ */
+public abstract class NodeToNormalizedNodeBuilder<T extends PathArgument>
+    implements Identifiable<T> {
+
+    private final T identifier;
+
+    protected static final Logger logger = LoggerFactory
+        .getLogger(NodeToNormalizedNodeBuilder.class);
+
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    }
+
+    ;
+
+    protected NodeToNormalizedNodeBuilder(final T identifier) {
+        super();
+        this.identifier = identifier;
+
+    }
+
+    /**
+     * @return Should return true if the node that this operation corresponds to is a mixin
+     */
+    public boolean isMixin() {
+        return false;
+    }
+
+
+    /**
+     * @return Should return true if the node that this operation corresponds to has a 'key'
+     * associated with it. This is typically true for a list-item or leaf-list entry in yang
+     */
+    public boolean isKeyedEntry() {
+        return false;
+    }
+
+    protected Set<QName> getQNameIdentifiers() {
+        return Collections.singleton(identifier.getNodeType());
+    }
+
+    public abstract NodeToNormalizedNodeBuilder<?> getChild(
+        final PathArgument child);
+
+    public abstract NodeToNormalizedNodeBuilder<?> getChild(QName child);
+
+    public abstract NormalizedNode<?, ?> normalize(QName nodeType, Node node);
+
+
+
+    private static abstract class SimpleTypeNormalization<T extends PathArgument>
+        extends NodeToNormalizedNodeBuilder<T> {
+
+        protected SimpleTypeNormalization(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public NormalizedNode<?, ?> normalize(final QName nodeType,
+            final Node node) {
+            checkArgument(node != null);
+            return normalizeImpl(nodeType, node);
+        }
+
+        protected abstract NormalizedNode<?, ?> normalizeImpl(QName nodeType,
+            Node node);
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            return null;
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            return null;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+    }
+
+
+    private static final class LeafNormalization extends
+        SimpleTypeNormalization<NodeIdentifier> {
+
+        private final LeafSchemaNode schema;
+
+        protected LeafNormalization(final LeafSchemaNode schema, final NodeIdentifier identifier) {
+            super(identifier);
+            this.schema = schema;
+        }
+
+        @Override
+        protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
+            final Node node) {
+            Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
+            return ImmutableNodes.leafNode(nodeType, value);
+
+        }
+
+    }
+
+
+    private static final class LeafListEntryNormalization extends
+        SimpleTypeNormalization<NodeWithValue> {
+
+        private final LeafListSchemaNode schema;
+
+        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new NodeWithValue(potential.getQName(), null));
+            this.schema = potential;
+        }
+
+        @Override
+        protected NormalizedNode<?, ?> normalizeImpl(final QName nodeType,
+            final Node node) {
+            final Object data = node.getValue();
+            if (data == null) {
+                Preconditions.checkArgument(false,
+                    "No data available in leaf list entry for " + nodeType);
+            }
+
+            Object value = NodeValueCodec.toTypeSafeValue(this.schema, this.schema.getType(), node);
+
+            NodeWithValue nodeId = new NodeWithValue(nodeType, value);
+            return Builders.leafSetEntryBuilder().withNodeIdentifier(nodeId)
+                .withValue(value).build();
+        }
+
+
+        @Override
+        public boolean isKeyedEntry() {
+            return true;
+        }
+    }
+
+
+    private static abstract class NodeToNormalizationNodeOperation<T extends PathArgument>
+        extends NodeToNormalizedNodeBuilder<T> {
+
+        protected NodeToNormalizationNodeOperation(final T identifier) {
+            super(identifier);
+        }
+
+        @SuppressWarnings({"rawtypes", "unchecked"})
+        @Override
+        public final NormalizedNodeContainer<?, ?, ?> normalize(
+            final QName nodeType, final Node node) {
+            checkArgument(node != null);
+
+            if (!node.getType().equals(AugmentationNode.class.getSimpleName())
+                && !node.getType().equals(ContainerNode.class.getSimpleName())
+                && !node.getType().equals(MapNode.class.getSimpleName())) {
+                checkArgument(nodeType != null);
+            }
+
+            NormalizedNodeContainerBuilder builder = createBuilder(node);
+
+            Set<NodeToNormalizedNodeBuilder<?>> usedMixins = new HashSet<>();
+
+            logNode(node);
+
+            if (node.getChildCount() == 0 && (
+                node.getType().equals(LeafSetEntryNode.class.getSimpleName())
+                    || node.getType().equals(LeafNode.class.getSimpleName()))) {
+                PathArgument childPathArgument =
+                    NodeIdentifierFactory.getArgument(node.getPath());
+
+                final NormalizedNode child;
+                if (childPathArgument instanceof NodeWithValue) {
+                    final NodeWithValue nodeWithValue =
+                        new NodeWithValue(childPathArgument.getNodeType(),
+                            node.getValue());
+                    child =
+                        Builders.leafSetEntryBuilder()
+                            .withNodeIdentifier(nodeWithValue)
+                            .withValue(node.getValue()).build();
+                } else {
+                    child =
+                        ImmutableNodes.leafNode(childPathArgument.getNodeType(),
+                            node.getValue());
+                }
+                builder.addChild(child);
+            }
+
+            final List<Node> children = node.getChildList();
+            for (Node nodeChild : children) {
+
+                PathArgument childPathArgument =
+                    NodeIdentifierFactory.getArgument(nodeChild.getPath());
+
+                QName childNodeType = null;
+                NodeToNormalizedNodeBuilder childOp = null;
+
+                if (childPathArgument instanceof AugmentationIdentifier) {
+                    childOp = getChild(childPathArgument);
+                    checkArgument(childOp instanceof AugmentationNormalization, childPathArgument);
+                } else {
+                    childNodeType = childPathArgument.getNodeType();
+                    childOp = getChild(childNodeType);
+                }
+                // We skip unknown nodes if this node is mixin since
+                // it's nodes and parent nodes are interleaved
+                if (childOp == null && isMixin()) {
+                    continue;
+                } else if (childOp == null) {
+                    logger.error(
+                        "childOp is null and this operation is not a mixin : this = {}",
+                        this.toString());
+                }
+
+                checkArgument(childOp != null,
+                    "Node %s is not allowed inside %s",
+                    childNodeType, getIdentifier());
+
+                if (childOp.isMixin()) {
+                    if (usedMixins.contains(childOp)) {
+                        // We already run / processed that mixin, so to avoid
+                        // duplicate we are
+                        // skipping next nodes.
+                        continue;
+                    }
+                    // builder.addChild(childOp.normalize(nodeType, treeCacheNode));
+                    final NormalizedNode childNode =
+                        childOp.normalize(childNodeType, nodeChild);
+                    if (childNode != null)
+                        builder.addChild(childNode);
+                    usedMixins.add(childOp);
+                } else {
+                    final NormalizedNode childNode =
+                        childOp.normalize(childNodeType, nodeChild);
+                    if (childNode != null)
+                        builder.addChild(childNode);
+                }
+            }
+
+
+            try {
+                return (NormalizedNodeContainer<?, ?, ?>) builder.build();
+            } catch (Exception e) {
+                return null;
+            }
+
+        }
+
+        private void logNode(Node node) {
+            //let us find out the type of the node
+            logger.debug("We got a {} , with identifier {} with {} children",
+                node.getType(), node.getPath(),
+                node.getChildList());
+        }
+
+        @SuppressWarnings("rawtypes")
+        protected abstract NormalizedNodeContainerBuilder createBuilder(
+            final Node node);
+
+    }
+
+
+    private static abstract class DataContainerNormalizationOperation<T extends PathArgument>
+        extends NodeToNormalizationNodeOperation<T> {
+
+        private final DataNodeContainer schema;
+        private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
+        private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
+
+        protected DataContainerNormalizationOperation(final T identifier,
+            final DataNodeContainer schema) {
+            super(identifier);
+            this.schema = schema;
+            this.byArg = new ConcurrentHashMap<>();
+            this.byQName = new ConcurrentHashMap<>();
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            NodeToNormalizedNodeBuilder<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromSchema(schema, child);
+            return register(potential);
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            if (child == null) {
+                return null;
+            }
+
+            NodeToNormalizedNodeBuilder<?> potential = byQName.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromSchemaAndPathArgument(schema, child);
+            return register(potential);
+        }
+
+        private NodeToNormalizedNodeBuilder<?> register(
+            final NodeToNormalizedNodeBuilder<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+                for (QName qName : potential.getQNameIdentifiers()) {
+                    byQName.put(qName, potential);
+                }
+            }
+            return potential;
+        }
+
+    }
+
+
+    private static final class ListItemNormalization extends
+        DataContainerNormalizationOperation<NodeIdentifierWithPredicates> {
+
+        private final List<QName> keyDefinition;
+        private final ListSchemaNode schemaNode;
+
+        protected ListItemNormalization(
+            final NodeIdentifierWithPredicates identifier,
+            final ListSchemaNode schema) {
+            super(identifier, schema);
+            this.schemaNode = schema;
+            keyDefinition = schema.getKeyDefinition();
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            NodeIdentifierWithPredicates nodeIdentifierWithPredicates =
+                (NodeIdentifierWithPredicates) NodeIdentifierFactory
+                    .createPathArgument(node
+                        .getPath(), schemaNode);
+            return Builders.mapEntryBuilder()
+                .withNodeIdentifier(
+                    nodeIdentifierWithPredicates
+            );
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode>
+                builder =
+                Builders.mapEntryBuilder().withNodeIdentifier(
+                    (NodeIdentifierWithPredicates) currentArg);
+            for (Entry<QName, Object> keyValue : ((NodeIdentifierWithPredicates) currentArg)
+                .getKeyValues().entrySet()) {
+                if (keyValue.getValue() == null) {
+                    throw new NullPointerException(
+                        "Null value found for path : "
+                            + currentArg);
+                }
+                builder.addChild(Builders.leafBuilder()
+                    //
+                    .withNodeIdentifier(new NodeIdentifier(keyValue.getKey()))
+                    .withValue(keyValue.getValue()).build());
+            }
+            return builder.build();
+        }
+
+
+        @Override
+        public boolean isKeyedEntry() {
+            return true;
+        }
+    }
+
+
+    private static final class ContainerNormalization extends
+        DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected ContainerNormalization(final ContainerSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            return Builders.containerBuilder()
+                .withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            return Builders.containerBuilder()
+                .withNodeIdentifier((NodeIdentifier) currentArg).build();
+        }
+
+    }
+
+
+    private static abstract class MixinNormalizationOp<T extends PathArgument>
+        extends NodeToNormalizationNodeOperation<T> {
+
+        protected MixinNormalizationOp(final T identifier) {
+            super(identifier);
+        }
+
+        @Override
+        public final boolean isMixin() {
+            return true;
+        }
+
+    }
+
+
+    private static final class LeafListMixinNormalization extends
+        MixinNormalizationOp<NodeIdentifier> {
+
+        private final NodeToNormalizedNodeBuilder<?> innerOp;
+
+        public LeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(new NodeIdentifier(potential.getQName()));
+            innerOp = new LeafListEntryNormalization(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            return Builders.leafSetBuilder()
+                .withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier())
+                .build();
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            if (child instanceof NodeWithValue) {
+                return innerOp;
+            }
+            return null;
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerOp;
+            }
+            return null;
+        }
+
+    }
+
+
+    private static final class AugmentationNormalization extends
+        MixinNormalizationOp<AugmentationIdentifier> {
+
+        private final Map<QName, NodeToNormalizedNodeBuilder<?>> byQName;
+        private final Map<PathArgument, NodeToNormalizedNodeBuilder<?>> byArg;
+
+        public AugmentationNormalization(final AugmentationSchema augmentation,
+            final DataNodeContainer schema) {
+            super(augmentationIdentifierFrom(augmentation));
+
+            ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
+                byQNameBuilder =
+                ImmutableMap.builder();
+            ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
+                byArgBuilder =
+                ImmutableMap.builder();
+
+            for (DataSchemaNode augNode : augmentation.getChildNodes()) {
+                DataSchemaNode resolvedNode =
+                    schema.getDataChildByName(augNode.getQName());
+                NodeToNormalizedNodeBuilder<?> resolvedOp =
+                    fromDataSchemaNode(resolvedNode);
+                byArgBuilder.put(resolvedOp.getIdentifier(), resolvedOp);
+                for (QName resQName : resolvedOp.getQNameIdentifiers()) {
+                    byQNameBuilder.put(resQName, resolvedOp);
+                }
+            }
+            byQName = byQNameBuilder.build();
+            byArg = byArgBuilder.build();
+
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            return byQName.get(child);
+        }
+
+        @Override
+        protected Set<QName> getQNameIdentifiers() {
+            return getIdentifier().getPossibleChildNames();
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            return Builders.augmentationBuilder()
+                .withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            return Builders.augmentationBuilder()
+                .withNodeIdentifier(getIdentifier())
+                .build();
+        }
+
+    }
+
+
+    private static final class ListMixinNormalization extends
+        MixinNormalizationOp<NodeIdentifier> {
+
+        private final ListItemNormalization innerNode;
+
+        public ListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode =
+                new ListItemNormalization(new NodeIdentifierWithPredicates(
+                    list.getQName(), Collections.<QName, Object>emptyMap()),
+                    list);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier())
+                .build();
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerNode;
+            }
+            return null;
+        }
+
+    }
+
+
+    private static class ChoiceNodeNormalization extends
+        MixinNormalizationOp<NodeIdentifier> {
+
+        private final ImmutableMap<QName, NodeToNormalizedNodeBuilder<?>>
+            byQName;
+        private final ImmutableMap<PathArgument, NodeToNormalizedNodeBuilder<?>>
+            byArg;
+
+        protected ChoiceNodeNormalization(
+            final org.opendaylight.yangtools.yang.model.api.ChoiceNode schema) {
+            super(new NodeIdentifier(schema.getQName()));
+            ImmutableMap.Builder<QName, NodeToNormalizedNodeBuilder<?>>
+                byQNameBuilder =
+                ImmutableMap.builder();
+            ImmutableMap.Builder<PathArgument, NodeToNormalizedNodeBuilder<?>>
+                byArgBuilder =
+                ImmutableMap.builder();
+
+            for (ChoiceCaseNode caze : schema.getCases()) {
+                for (DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    NodeToNormalizedNodeBuilder<?> childOp =
+                        fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                    for (QName qname : childOp.getQNameIdentifiers()) {
+                        byQNameBuilder.put(qname, childOp);
+                    }
+                }
+            }
+            byQName = byQNameBuilder.build();
+            byArg = byArgBuilder.build();
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(
+            final PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        public NodeToNormalizedNodeBuilder<?> getChild(final QName child) {
+            return byQName.get(child);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(
+            final Node node) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(
+            final PathArgument currentArg) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier())
+                .build();
+        }
+    }
+
+    /**
+     * Find an appropriate NormalizedNodeBuilder using both the schema and the
+     * Path Argument
+     *
+     * @param schema
+     * @param child
+     * @return
+     */
+    public static NodeToNormalizedNodeBuilder<?> fromSchemaAndPathArgument(
+        final DataNodeContainer schema, final QName child) {
+        DataSchemaNode potential = schema.getDataChildByName(child);
+        if (potential == null) {
+            Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode>
+                choices =
+                FluentIterable.from(schema.getChildNodes()).filter(
+                    org.opendaylight.yangtools.yang.model.api.ChoiceNode.class);
+            potential = findChoice(choices, child);
+        }
+        if (potential == null) {
+            if (logger.isTraceEnabled()) {
+                logger.trace("BAD CHILD = {}", child.toString());
+            }
+        }
+
+        checkArgument(potential != null,
+            "Supplied QName %s is not valid according to schema %s", child,
+            schema);
+
+        // If the schema in an instance of DataSchemaNode and the potential
+        // is augmenting something then there is a chance that this may be
+        // and augmentation node
+        if ((schema instanceof DataSchemaNode)
+            && potential.isAugmenting()) {
+
+            AugmentationNormalization augmentation =
+                fromAugmentation(schema, (AugmentationTarget) schema,
+                    potential);
+
+            // If an augmentation normalization (builder) is not found then
+            // we fall through to the regular processing
+            if(augmentation != null){
+                return augmentation;
+            }
+        }
+        return fromDataSchemaNode(potential);
+    }
+
+    /**
+     * Given a bunch of choice nodes and a the name of child find a choice node for that child which
+     * has a non-null value
+     *
+     * @param choices
+     * @param child
+     * @return
+     */
+    private static org.opendaylight.yangtools.yang.model.api.ChoiceNode findChoice(
+        final Iterable<org.opendaylight.yangtools.yang.model.api.ChoiceNode> choices,
+        final QName child) {
+        org.opendaylight.yangtools.yang.model.api.ChoiceNode foundChoice = null;
+        choiceLoop:
+        for (org.opendaylight.yangtools.yang.model.api.ChoiceNode choice : choices) {
+            for (ChoiceCaseNode caze : choice.getCases()) {
+                if (caze.getDataChildByName(child) != null) {
+                    foundChoice = choice;
+                    break choiceLoop;
+                }
+            }
+        }
+        return foundChoice;
+    }
+
+
+    /**
+     * Create an AugmentationIdentifier based on the AugmentationSchema
+     *
+     * @param augmentation
+     * @return
+     */
+    public static AugmentationIdentifier augmentationIdentifierFrom(
+        final AugmentationSchema augmentation) {
+        ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+        for (DataSchemaNode child : augmentation.getChildNodes()) {
+            potentialChildren.add(child.getQName());
+        }
+        return new AugmentationIdentifier(potentialChildren.build());
+    }
+
+    /**
+     * Create an AugmentationNormalization based on the schema of the DataContainer, the
+     * AugmentationTarget and the potential schema node
+     *
+     * @param schema
+     * @param augments
+     * @param potential
+     * @return
+     */
+    private static AugmentationNormalization fromAugmentation(
+        final DataNodeContainer schema, final AugmentationTarget augments,
+        final DataSchemaNode potential) {
+        AugmentationSchema augmentation = null;
+        for (AugmentationSchema aug : augments.getAvailableAugmentations()) {
+            DataSchemaNode child = aug.getDataChildByName(potential.getQName());
+            if (child != null) {
+                augmentation = aug;
+                break;
+            }
+
+        }
+        if (augmentation != null) {
+            return new AugmentationNormalization(augmentation, schema);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * @param schema
+     * @param child
+     * @return
+     */
+    private static NodeToNormalizedNodeBuilder<?> fromSchema(
+        final DataNodeContainer schema, final PathArgument child) {
+        if (child instanceof AugmentationIdentifier) {
+            QName childQName = ((AugmentationIdentifier) child)
+                .getPossibleChildNames().iterator().next();
+
+            return fromSchemaAndPathArgument(schema, childQName);
+        }
+        return fromSchemaAndPathArgument(schema, child.getNodeType());
+    }
+
+    public static NodeToNormalizedNodeBuilder<?> fromDataSchemaNode(
+        final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new ContainerNormalization((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return new ListMixinNormalization((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new LeafNormalization((LeafSchemaNode) potential,
+                new NodeIdentifier(potential.getQName()));
+        } else if (potential instanceof org.opendaylight.yangtools.yang.model.api.ChoiceNode) {
+            return new ChoiceNodeNormalization(
+                (org.opendaylight.yangtools.yang.model.api.ChoiceNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return new LeafListMixinNormalization(
+                (LeafListSchemaNode) potential);
+        }
+        return null;
+    }
+
+    public static NodeToNormalizedNodeBuilder<?> from(final SchemaContext ctx) {
+        return new ContainerNormalization(ctx);
+    }
+
+    public abstract NormalizedNode<?, ?> createDefault(PathArgument currentArg);
+
+}