Merge "Introduce InstanceIdToNodes into yang-data-impl"
authorTony Tkacik <ttkacik@cisco.com>
Wed, 22 Apr 2015 18:25:56 +0000 (18:25 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 22 Apr 2015 18:25:57 +0000 (18:25 +0000)
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToCompositeNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/resources/filter-test.yang [new file with mode: 0644]

index 0fd44f478068ab4415afdc656cc04e89776a1b33..c7013c4a706ed9019cd4b0f31600a03fc2310224 100644 (file)
@@ -7,7 +7,12 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema;
 
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.util.Map;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 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.schema.ChoiceNode;
@@ -15,6 +20,7 @@ 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.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.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.impl.ImmutableChoiceNodeBuilder;
@@ -22,6 +28,8 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableCo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 public final class ImmutableNodes {
 
@@ -83,4 +91,46 @@ public final class ImmutableNodes {
     public static ChoiceNode choiceNode(final QName name) {
         return ImmutableChoiceNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(name)).build();
     }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @return serialized normalized node for provided instance Id
+     */
+    public static NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx, final YangInstanceIdentifier id) {
+        return fromInstanceId(ctx, id, Optional.<NormalizedNode<?, ?>>absent(), Optional.<Map.Entry<QName, ModifyAction>>absent());
+    }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @param deepestElement pre-built deepest child that will be inserted at the last path argument of provided instance Id
+     * @return serialized normalized node for provided instance Id with overridden last child.
+     */
+    public static NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx, final YangInstanceIdentifier id, final NormalizedNode<?, ?> deepestElement) {
+        return fromInstanceId(ctx, id, Optional.<NormalizedNode<?, ?>>of(deepestElement), Optional.<Map.Entry<QName, ModifyAction>>absent());
+    }
+
+    /**
+     * Convert YangInstanceIdentifier into a normalized node structure
+     *
+     * @param ctx schema context to used during serialization
+     * @param id instance identifier to convert to node structure starting from root
+     * @param deepestElement pre-built deepest child that will be inserted at the last path argument of provided instance Id
+     * @param operation modify operation attribute to be added to the deepest child. QName is the operation attribute key and ModifyAction is the value.
+     * @return serialized normalized node for provided instance Id with (optionally) overridden last child and (optionally) marked with specific operation attribute.
+     */
+    public static NormalizedNode<?, ?> fromInstanceId(final SchemaContext ctx, final YangInstanceIdentifier id, final Optional<NormalizedNode<?, ?>> deepestElement, final Optional<Map.Entry<QName, ModifyAction>> operation) {
+        Preconditions.checkNotNull(ctx);
+        Preconditions.checkNotNull(id);
+        final YangInstanceIdentifier.PathArgument topLevelElement = id.getPathArguments().iterator().next();
+        final DataSchemaNode dataChildByName = ctx.getDataChildByName(topLevelElement.getNodeType());
+        Preconditions.checkNotNull(dataChildByName, "Cannot find %s node in schema context. Instance identifier has to start from root", topLevelElement);
+        final InstanceIdToNodes<?> instanceIdToNodes = InstanceIdToNodes.fromSchemaAndQNameChecked(ctx, topLevelElement.getNodeType());
+        return instanceIdToNodes.create(id, deepestElement, operation);
+    }
 }
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToCompositeNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToCompositeNodes.java
new file mode 100644 (file)
index 0000000..28e8d89
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * 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.yangtools.yang.data.impl.schema;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
+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.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+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.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.util.EffectiveAugmentationSchema;
+
+/**
+* Base strategy for converting an instance identifier into a normalized node structure for container-like types.
+*/
+abstract class InstanceIdToCompositeNodes<T extends YangInstanceIdentifier.PathArgument> extends
+        InstanceIdToNodes<T> {
+
+    protected InstanceIdToCompositeNodes(final T identifier) {
+        super(identifier);
+    }
+
+    private static YangInstanceIdentifier.AugmentationIdentifier augmentationIdentifierFrom(final AugmentationSchema augmentation) {
+        final ImmutableSet.Builder<QName> potentialChildren = ImmutableSet.builder();
+        for (final DataSchemaNode child : augmentation.getChildNodes()) {
+            potentialChildren.add(child.getQName());
+        }
+        return new YangInstanceIdentifier.AugmentationIdentifier(potentialChildren.build());
+    }
+
+    private static DataNodeContainer augmentationProxy(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+        final Set<DataSchemaNode> children = new HashSet<>();
+        for (final DataSchemaNode augNode : augmentation.getChildNodes()) {
+            children.add(schema.getDataChildByName(augNode.getQName()));
+        }
+        return new EffectiveAugmentationSchema(augmentation, children);
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public final NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId, final Optional<NormalizedNode<?, ?>> lastChild, final Optional<Map.Entry<QName,ModifyAction>> operation) {
+        checkNotNull(instanceId);
+        final Iterator<YangInstanceIdentifier.PathArgument> iterator = instanceId.getPathArguments().iterator();
+        final YangInstanceIdentifier.PathArgument legacyData = iterator.next();
+
+        if (!isMixin(this) && getIdentifier().getNodeType() != null) {
+            checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
+                    "Node QName must be %s was %s", getIdentifier().getNodeType(), legacyData.getNodeType());
+        }
+        final NormalizedNodeContainerBuilder builder = createBuilder(legacyData);
+
+        if (iterator.hasNext()) {
+            final YangInstanceIdentifier.PathArgument childPath = iterator.next();
+            final InstanceIdToNodes childOp = getChildOperation(childPath);
+
+            final YangInstanceIdentifier childId = YangInstanceIdentifier.create(Iterables.skip(instanceId.getPathArguments(), 1));
+            builder.addChild(childOp.create(childId, lastChild, operation));
+        } else {
+            if(lastChild.isPresent()) {
+                builder.withValue(Lists.newArrayList((Collection<?>) lastChild.get().getValue()));
+            }
+            if(operation.isPresent()) {
+                Preconditions.checkArgument(builder instanceof AttributesBuilder<?>);
+                addModifyOpIfPresent(operation, ((AttributesBuilder<?>) builder));
+            }
+        }
+
+        return builder.build();
+    }
+
+    private InstanceIdToNodes getChildOperation(final YangInstanceIdentifier.PathArgument childPath) {
+        final InstanceIdToNodes childOp;
+        try {
+            childOp = getChild(childPath);
+        } catch (final RuntimeException e) {
+            throw new IllegalArgumentException(String.format("Failed to process child node %s", childPath), e);
+        }
+        checkArgument(childOp != null, "Node %s is not allowed inside %s", childPath, getIdentifier());
+        return childOp;
+    }
+
+    @SuppressWarnings("rawtypes")
+    protected abstract NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode);
+
+    static abstract class DataContainerNormalizationOperation<T extends YangInstanceIdentifier.PathArgument> extends
+            InstanceIdToCompositeNodes<T> {
+
+        private final DataNodeContainer schema;
+        private final Map<YangInstanceIdentifier.PathArgument, InstanceIdToNodes<?>> byArg;
+
+        protected DataContainerNormalizationOperation(final T identifier, final DataNodeContainer schema) {
+            super(identifier);
+            this.schema = schema;
+            this.byArg = new ConcurrentHashMap<>();
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final YangInstanceIdentifier.PathArgument child) {
+            InstanceIdToNodes<?> potential = byArg.get(child);
+            if (potential != null) {
+                return potential;
+            }
+            potential = fromLocalSchema(child);
+            return register(potential);
+        }
+
+        private InstanceIdToNodes<?> fromLocalSchema(final YangInstanceIdentifier.PathArgument child) {
+            if (child instanceof YangInstanceIdentifier.AugmentationIdentifier) {
+                return fromSchemaAndQNameChecked(schema, ((YangInstanceIdentifier.AugmentationIdentifier) child).getPossibleChildNames()
+                        .iterator().next());
+            }
+            return fromSchemaAndQNameChecked(schema, child.getNodeType());
+        }
+
+        private InstanceIdToNodes<?> register(final InstanceIdToNodes<?> potential) {
+            if (potential != null) {
+                byArg.put(potential.getIdentifier(), potential);
+            }
+            return potential;
+        }
+    }
+
+    static final class ListItemNormalization extends
+            DataContainerNormalizationOperation<YangInstanceIdentifier.NodeIdentifierWithPredicates> {
+
+        protected ListItemNormalization(final YangInstanceIdentifier.NodeIdentifierWithPredicates identifier, final ListSchemaNode schema) {
+            super(identifier, schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument currentArg) {
+            final DataContainerNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> builder = Builders
+                    .mapEntryBuilder().withNodeIdentifier((YangInstanceIdentifier.NodeIdentifierWithPredicates) currentArg);
+            for (final Map.Entry<QName, Object> keyValue : ((YangInstanceIdentifier.NodeIdentifierWithPredicates) currentArg).getKeyValues().entrySet()) {
+                builder.addChild(Builders.leafBuilder()
+                        //
+                        .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(keyValue.getKey())).withValue(keyValue.getValue())
+                        .build());
+            }
+            return builder;
+        }
+
+    }
+
+    static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<YangInstanceIdentifier.NodeIdentifier> {
+
+        protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+            super(new YangInstanceIdentifier.NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+    }
+
+    static final class ContainerTransformation extends DataContainerNormalizationOperation<YangInstanceIdentifier.NodeIdentifier> {
+
+        protected ContainerTransformation(final ContainerSchemaNode schema) {
+            super(new YangInstanceIdentifier.NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.containerBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+
+
+        public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static class UnorderedLeafListMixinNormalization extends InstanceIdToCompositeNodes<YangInstanceIdentifier.NodeIdentifier> implements MixinNormalizationOp {
+
+        private final InstanceIdToNodes<?> innerOp;
+
+        public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(new YangInstanceIdentifier.NodeIdentifier(potential.getQName()));
+            innerOp = new InstanceIdToSimpleNodes.LeafListEntryNormalization(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.leafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final YangInstanceIdentifier.PathArgument child) {
+            if (child instanceof YangInstanceIdentifier.NodeWithValue) {
+                return innerOp;
+            }
+            return null;
+        }
+    }
+
+    static final class AugmentationNormalization extends DataContainerNormalizationOperation<YangInstanceIdentifier.AugmentationIdentifier> implements MixinNormalizationOp {
+
+        public AugmentationNormalization(final AugmentationSchema augmentation, final DataNodeContainer schema) {
+            super(augmentationIdentifierFrom(augmentation), augmentationProxy(augmentation, schema));
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.augmentationBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static class UnorderedMapMixinNormalization extends InstanceIdToCompositeNodes<YangInstanceIdentifier.NodeIdentifier> implements MixinNormalizationOp {
+
+        private final ListItemNormalization innerNode;
+
+        public UnorderedMapMixinNormalization(final ListSchemaNode list) {
+            super(new YangInstanceIdentifier.NodeIdentifier(list.getQName()));
+            this.innerNode = new ListItemNormalization(new YangInstanceIdentifier.NodeIdentifierWithPredicates(list.getQName(),
+                    Collections.<QName, Object>emptyMap()), list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.mapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final YangInstanceIdentifier.PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+    }
+
+    static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+
+        public OrderedMapMixinNormalization(final ListSchemaNode list) {
+            super(list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+    }
+
+    static class ChoiceNodeNormalization extends InstanceIdToCompositeNodes<YangInstanceIdentifier.NodeIdentifier> implements MixinNormalizationOp {
+
+        private final ImmutableMap<YangInstanceIdentifier.PathArgument, InstanceIdToNodes<?>> byArg;
+
+        protected ChoiceNodeNormalization(final ChoiceSchemaNode schema) {
+            super(new YangInstanceIdentifier.NodeIdentifier(schema.getQName()));
+            final ImmutableMap.Builder<YangInstanceIdentifier.PathArgument, InstanceIdToNodes<?>> byArgBuilder = ImmutableMap.builder();
+
+            for (final ChoiceCaseNode caze : schema.getCases()) {
+                for (final DataSchemaNode cazeChild : caze.getChildNodes()) {
+                    final InstanceIdToNodes<?> childOp = fromDataSchemaNode(cazeChild);
+                    byArgBuilder.put(childOp.getIdentifier(), childOp);
+                }
+            }
+            byArg = byArgBuilder.build();
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final YangInstanceIdentifier.PathArgument child) {
+            return byArg.get(child);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final YangInstanceIdentifier.PathArgument compositeNode) {
+            return Builders.choiceBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodes.java
new file mode 100644 (file)
index 0000000..0185f57
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * 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.yangtools.yang.data.impl.schema;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.FluentIterable;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map.Entry;
+import javax.xml.transform.dom.DOMSource;
+import org.opendaylight.yangtools.concepts.Identifiable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.AttributesBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+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.ChoiceSchemaNode;
+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;
+
+/**
+ * Base strategy for converting an instance identifier into a normalized node structure.
+ * Use provided static methods for generic YangInstanceIdentifier -> NormalizedNode translation in ImmutableNodes.
+ */
+abstract class InstanceIdToNodes<T extends PathArgument> implements Identifiable<T> {
+
+    private final T identifier;
+
+    @Override
+    public T getIdentifier() {
+        return identifier;
+    }
+
+    protected InstanceIdToNodes(final T identifier) {
+        this.identifier = identifier;
+    }
+
+    /**
+     * Build a strategy for the next path argument
+     *
+     * @param child child identifier
+     * @return transformation strategy for a specific child
+     */
+    abstract InstanceIdToNodes<?> getChild(final PathArgument child);
+
+    /**
+     *
+     * Convert instance identifier into a NormalizedNode structure
+     *
+     * @param instanceId Instance identifier to transform into NormalizedNodes
+     * @param deepestChild Optional normalized node to be inserted as the last child
+     * @param operation Optional modify operation to be set on the last child
+     * @return NormalizedNode structure corresponding to submitted instance ID
+     */
+    abstract NormalizedNode<?, ?> create(YangInstanceIdentifier instanceId, Optional<NormalizedNode<?, ?>> deepestChild, Optional<Entry<QName,ModifyAction>> operation);
+
+
+    public void addModifyOpIfPresent(final Optional<Entry<QName,ModifyAction>> operation, final AttributesBuilder<?> builder) {
+        if(operation.isPresent()) {
+            builder.withAttributes(Collections.singletonMap(operation.get().getKey(), modifyOperationToXmlString(operation.get().getValue())));
+        }
+    }
+
+    public static String modifyOperationToXmlString(final ModifyAction operation) {
+        return operation.name().toLowerCase();
+    }
+
+    static boolean isMixin(final InstanceIdToNodes<?> op) {
+        return op instanceof MixinNormalizationOp;
+    }
+
+    /**
+     * Marker interface for Mixin nodes normalization operations
+     */
+    interface MixinNormalizationOp {}
+
+
+    private static class UnkeyedListMixinNormalization extends InstanceIdToCompositeNodes<NodeIdentifier> implements MixinNormalizationOp {
+
+        private final UnkeyedListItemNormalization innerNode;
+
+        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode = new UnkeyedListItemNormalization(list);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder<?, ?, ?, ?> createBuilder(final PathArgument compositeNode) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+    }
+
+    private static class AnyXmlNormalization extends InstanceIdToNodes<NodeIdentifier> {
+
+        protected AnyXmlNormalization(final AnyXmlSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()));
+        }
+
+        @Override
+        public InstanceIdToNodes<?> getChild(final PathArgument child) {
+            return null;
+        }
+
+        @Override
+        public NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<Entry<QName,ModifyAction>> operation) {
+            if(deepestChild.isPresent()) {
+                Preconditions.checkState(deepestChild instanceof AnyXmlNode);
+                final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> anyXmlBuilder =
+                        Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier()).withValue(((AnyXmlNode) deepestChild).getValue());
+                addModifyOpIfPresent(operation, anyXmlBuilder);
+                return anyXmlBuilder.build();
+            }
+
+            final NormalizedNodeAttrBuilder<NodeIdentifier, DOMSource, AnyXmlNode> builder =
+                    Builders.anyXmlBuilder().withNodeIdentifier(getIdentifier());
+            addModifyOpIfPresent(operation, builder);
+            return builder.build();
+        }
+
+    }
+
+    private static Optional<DataSchemaNode> findChildSchemaNode(final DataNodeContainer parent, final QName child) {
+        DataSchemaNode potential = parent.getDataChildByName(child);
+        if (potential == null) {
+            final Iterable<ChoiceSchemaNode> choices = FluentIterable.from(parent.getChildNodes()).filter(ChoiceSchemaNode.class);
+            potential = findChoice(choices, child);
+        }
+        return Optional.fromNullable(potential);
+    }
+
+    static InstanceIdToNodes<?> fromSchemaAndQNameChecked(final DataNodeContainer schema, final QName child) {
+        final Optional<DataSchemaNode> potential = findChildSchemaNode(schema, child);
+        Preconditions.checkArgument(potential.isPresent(),
+                "Supplied QName %s is not valid according to schema %s, potential children nodes: %s", child, schema, schema.getChildNodes());
+
+        final DataSchemaNode result = potential.get();
+        // We try to look up if this node was added by augmentation
+        if ((schema instanceof DataSchemaNode) && result.isAugmenting()) {
+            return fromAugmentation(schema, (AugmentationTarget) schema, result);
+        }
+        return fromDataSchemaNode(result);
+    }
+
+    private static ChoiceSchemaNode findChoice(final Iterable<ChoiceSchemaNode> choices, final QName child) {
+        ChoiceSchemaNode foundChoice = null;
+        choiceLoop:
+        for (final ChoiceSchemaNode choice : choices) {
+            for (final ChoiceCaseNode caze : choice.getCases()) {
+                if (findChildSchemaNode(caze, child).isPresent()) {
+                    foundChoice = choice;
+                    break choiceLoop;
+                }
+            }
+        }
+        return foundChoice;
+    }
+
+    /**
+     * Returns a SchemaPathUtil for provided child node
+     * <p/>
+     * If supplied child is added by Augmentation this operation returns
+     * a SchemaPathUtil for augmentation,
+     * otherwise returns a SchemaPathUtil for child as
+     * call for {@link #fromDataSchemaNode(org.opendaylight.yangtools.yang.model.api.DataSchemaNode)}.
+     */
+    private static InstanceIdToNodes<?> fromAugmentation(final DataNodeContainer parent,
+                                                          final AugmentationTarget parentAug, final DataSchemaNode child) {
+        AugmentationSchema augmentation = null;
+        for (final AugmentationSchema aug : parentAug.getAvailableAugmentations()) {
+            final DataSchemaNode potential = aug.getDataChildByName(child.getQName());
+            if (potential != null) {
+                augmentation = aug;
+                break;
+            }
+
+        }
+        if (augmentation != null) {
+            return new InstanceIdToCompositeNodes.AugmentationNormalization(augmentation, parent);
+        } else {
+            return fromDataSchemaNode(child);
+        }
+    }
+
+    static InstanceIdToNodes<?> fromDataSchemaNode(final DataSchemaNode potential) {
+        if (potential instanceof ContainerSchemaNode) {
+            return new InstanceIdToCompositeNodes.ContainerTransformation((ContainerSchemaNode) potential);
+        } else if (potential instanceof ListSchemaNode) {
+            return fromListSchemaNode((ListSchemaNode) potential);
+        } else if (potential instanceof LeafSchemaNode) {
+            return new InstanceIdToSimpleNodes.LeafNormalization((LeafSchemaNode) potential);
+        } else if (potential instanceof ChoiceSchemaNode) {
+            return new InstanceIdToCompositeNodes.ChoiceNodeNormalization((ChoiceSchemaNode) potential);
+        } else if (potential instanceof LeafListSchemaNode) {
+            return fromLeafListSchemaNode((LeafListSchemaNode) potential);
+        } else if (potential instanceof AnyXmlSchemaNode) {
+            return new AnyXmlNormalization((AnyXmlSchemaNode) potential);
+        }
+        return null;
+    }
+
+    private static InstanceIdToNodes<?> fromListSchemaNode(final ListSchemaNode potential) {
+        final List<QName> keyDefinition = potential.getKeyDefinition();
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinNormalization(potential);
+        }
+        if (potential.isUserOrdered()) {
+            return new InstanceIdToCompositeNodes.OrderedMapMixinNormalization(potential);
+        }
+        return new InstanceIdToCompositeNodes.UnorderedMapMixinNormalization(potential);
+    }
+
+    private static InstanceIdToNodes<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        if (potential.isUserOrdered()) {
+            return new InstanceIdToCompositeNodes.OrderedLeafListMixinNormalization(potential);
+        }
+        return new InstanceIdToCompositeNodes.UnorderedLeafListMixinNormalization(potential);
+    }
+
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToSimpleNodes.java
new file mode 100644 (file)
index 0000000..7a3e86a
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.yangtools.yang.data.impl.schema;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Iterables;
+import java.util.Map;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+
+/**
+* Base strategy for converting an instance identifier into a normalized node structure for leaf and leaf-list types.
+*/
+abstract class InstanceIdToSimpleNodes<T extends YangInstanceIdentifier.PathArgument> extends InstanceIdToNodes<T> {
+
+    protected InstanceIdToSimpleNodes(final T identifier) {
+        super(identifier);
+    }
+
+    @Override
+    public NormalizedNode<?, ?> create(final YangInstanceIdentifier instanceId, final Optional<NormalizedNode<?, ?>> deepestChild, final Optional<Map.Entry<QName,ModifyAction>> operation) {
+        checkNotNull(instanceId);
+        final YangInstanceIdentifier.PathArgument pathArgument = Iterables.get(instanceId.getPathArguments(), 0);
+        final NormalizedNodeAttrBuilder<? extends YangInstanceIdentifier.PathArgument, Object, ? extends NormalizedNode<? extends YangInstanceIdentifier.PathArgument, Object>> builder = getBuilder(pathArgument);
+
+        if(deepestChild.isPresent()) {
+            builder.withValue(deepestChild.get().getValue());
+        }
+
+        addModifyOpIfPresent(operation, builder);
+        return builder.build();
+    }
+
+    protected abstract NormalizedNodeAttrBuilder<? extends YangInstanceIdentifier.PathArgument, Object, ? extends NormalizedNode<? extends YangInstanceIdentifier.PathArgument, Object>> getBuilder(YangInstanceIdentifier.PathArgument node);
+
+    @Override
+    public InstanceIdToNodes<?> getChild(final YangInstanceIdentifier.PathArgument child) {
+        return null;
+    }
+
+    static final class LeafNormalization extends InstanceIdToSimpleNodes<YangInstanceIdentifier.NodeIdentifier> {
+
+        protected LeafNormalization(final LeafSchemaNode potential) {
+            super(new YangInstanceIdentifier.NodeIdentifier(potential.getQName()));
+        }
+
+        @Override
+        protected NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> getBuilder(final YangInstanceIdentifier.PathArgument node) {
+            return Builders.leafBuilder().withNodeIdentifier(getIdentifier());
+        }
+    }
+
+    static final class LeafListEntryNormalization extends InstanceIdToSimpleNodes<YangInstanceIdentifier.NodeWithValue> {
+
+        public LeafListEntryNormalization(final LeafListSchemaNode potential) {
+            super(new YangInstanceIdentifier.NodeWithValue(potential.getQName(), null));
+        }
+
+        @Override
+        protected NormalizedNodeAttrBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>> getBuilder(final YangInstanceIdentifier.PathArgument node) {
+            Preconditions.checkArgument(node instanceof YangInstanceIdentifier.NodeWithValue);
+            return Builders.leafSetEntryBuilder().withNodeIdentifier((YangInstanceIdentifier.NodeWithValue) node).withValue(((YangInstanceIdentifier.NodeWithValue) node).getValue());
+        }
+
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/InstanceIdToNodesTest.java
new file mode 100644 (file)
index 0000000..5735ea0
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * 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.yangtools.yang.data.impl.schema;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Collections2;
+import com.google.common.io.ByteSource;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+
+public class InstanceIdToNodesTest {
+
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    private static final String REVISION = "2014-03-13";
+    private static final QName ID = QName.create(NS, REVISION, "id");
+    private SchemaContext ctx;
+
+    private final YangInstanceIdentifier.NodeIdentifier rootContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "test"));
+    private final YangInstanceIdentifier.NodeIdentifier outerContainer = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-container"));
+    private final YangInstanceIdentifier.NodeIdentifier augmentedLeaf = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "augmented-leaf"));
+    private final YangInstanceIdentifier.AugmentationIdentifier augmentation = new YangInstanceIdentifier.AugmentationIdentifier(Collections.singleton(augmentedLeaf.getNodeType()));
+
+    private final YangInstanceIdentifier.NodeIdentifier outerList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-list"));
+    private final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListWithKey = new YangInstanceIdentifier.NodeIdentifierWithPredicates(QName.create(NS, REVISION, "outer-list"), ID, 1);
+    private final YangInstanceIdentifier.NodeIdentifier choice = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "outer-choice"));
+    private final YangInstanceIdentifier.NodeIdentifier leafFromCase = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "one"));
+
+    private final YangInstanceIdentifier.NodeIdentifier leafList = new YangInstanceIdentifier.NodeIdentifier(QName.create(NS, REVISION, "ordered-leaf-list"));
+    private final YangInstanceIdentifier.NodeWithValue leafListWithValue = new YangInstanceIdentifier.NodeWithValue(leafList.getNodeType(), "abcd");
+
+    static SchemaContext createTestContext() throws IOException, YangSyntaxErrorException {
+        final YangParserImpl parser = new YangParserImpl();
+        return parser.parseSources(Collections2.transform(Collections.singletonList("/filter-test.yang"), new Function<String, ByteSource>() {
+            @Override
+            public ByteSource apply(final String input) {
+                return new ByteSource() {
+                    @Override
+                    public InputStream openStream() throws IOException {
+                        return InstanceIdToNodesTest.class.getResourceAsStream(input);
+                    }
+                };
+            }
+        }));
+    }
+
+    @Before
+    public void setUp() throws Exception {
+        ctx = createTestContext();
+
+    }
+
+    @Test
+    public void testInAugment() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testInAugmentLeafOverride() throws Exception {
+        final LeafNode<Object> lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf).withValue("randomValue").build();
+
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.containerBuilder().withNodeIdentifier(outerContainer).withChild(
+                        Builders.augmentationBuilder().withNodeIdentifier(augmentation).withChild(
+                                lastLeaf
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testListChoice() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testTopContainerLastChildOverride() throws Exception {
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.mapBuilder().withNodeIdentifier(outerList).withChild(
+                        Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+                        ).withChild(
+                                Builders.choiceBuilder().withNodeIdentifier(choice).withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(leafFromCase).build()
+                                ).build()
+                        ).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer), expectedStructure);
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testListLastChildOverride() throws Exception {
+        final MapEntryNode outerListEntry = Builders.mapEntryBuilder().withNodeIdentifier(outerListWithKey).withChild(
+                Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID)).withValue(1).build()
+        ).build();
+        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(
+                outerListEntry
+        ).build();
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                lastChild
+        ).build();
+
+        NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
+        assertEquals(expectedStructure, filter);
+        filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testLeafList() throws Exception {
+        final ContainerNode expectedFilter = Builders.containerBuilder().withNodeIdentifier(rootContainer).withChild(
+                Builders.orderedLeafSetBuilder().withNodeIdentifier(leafList).withChild(
+                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue).withValue(leafListWithValue.getValue()).build()
+                ).build()
+        ).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx, YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
+        assertEquals(expectedFilter, filter);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/resources/filter-test.yang b/yang/yang-data-impl/src/test/resources/filter-test.yang
new file mode 100644 (file)
index 0000000..6df5306
--- /dev/null
@@ -0,0 +1,74 @@
+module normalization-test {
+    yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    prefix "norm-test";
+
+    revision "2014-03-13" {
+        description "Initial revision.";
+    }
+
+    grouping outer-grouping {
+    }
+
+    container test {
+        list outer-list {
+            key id;
+            leaf id {
+                type uint16;
+            }
+            choice outer-choice {
+                case one {
+                    leaf one {
+                        type string;
+                    }
+                }
+                case two-three {
+                    leaf two {
+                        type string;
+                    }
+                    leaf three {
+                        type string;
+                    }
+               }
+           }
+           list inner-list {
+                key name;
+                ordered-by user;
+
+                leaf name {
+                    type string;
+                }
+                leaf value {
+                    type string;
+                }
+            }
+        }
+
+        list unkeyed-list {
+            leaf name {
+                type string;
+            }
+        }
+
+        leaf-list unordered-leaf-list {
+            type string;
+        }
+
+        leaf-list ordered-leaf-list {
+            ordered-by user;
+            type string;
+        }
+
+        container outer-container {
+        }
+
+        anyxml any-xml-data;
+    }
+
+    augment /norm-test:test/norm-test:outer-container {
+
+        leaf augmented-leaf {
+           type string;
+        }
+    }
+}
\ No newline at end of file