+/*
+ *
+ * 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);
+
+}