BUG-509, BUG-808: Added datastore support for lists without keys. 11/6311/1
authorRobert Varga <rovarga@cisco.com>
Tue, 22 Apr 2014 12:54:04 +0000 (14:54 +0200)
committerRobert Varga <rovarga@cisco.com>
Tue, 22 Apr 2014 13:19:35 +0000 (15:19 +0200)
The datastore needs to support users who do not specify a list key, but
how expect the item ordering to be retained.

Added test which test all three possible behaviours of list
statement: ordered map, unordered map and list without keys.

Change-Id: I9e136267f57a88e4d2a6a4476025c33497373518
Signed-off-by: Tony Tkacik <ttkacik@cisco.com>
Signed-off-by: Robert Varga <rovarga@cisco.com>
opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java
opendaylight/md-sal/sal-binding-dom-it/pom.xml
opendaylight/md-sal/sal-binding-dom-it/src/main/yang/opendaylight-sal-test-store.yang [new file with mode: 0644]
opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ListProcessingAndOrderingTest.java [new file with mode: 0644]
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java
opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java
opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java

index d6d87ba..9cdf703 100644 (file)
@@ -118,6 +118,10 @@ public class BindingTestContext implements AutoCloseable, SchemaContextProvider
         return schemaContext;
     }
 
+    public DOMDataBroker getDomAsyncDataBroker() {
+        return newDOMDataBroker;
+    }
+
     protected BindingTestContext(final ListeningExecutorService executor, final ClassPool classPool, final boolean startWithSchema) {
         this.executor = executor;
         this.classPool = classPool;
index 099b77c..21fa207 100644 (file)
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+    <groupId>org.opendaylight.yangtools</groupId>
+    <artifactId>yang-binding</artifactId>
+    </dependency>
     <dependency>
       <groupId>org.opendaylight.controller.model</groupId>
       <artifactId>model-flow-management</artifactId>
   </dependencies>
   <build>
     <plugins>
-      <plugin>
-        <groupId>org.eclipse.xtend</groupId>
-        <artifactId>xtend-maven-plugin</artifactId>
-      </plugin>
+    <plugin>
+        <groupId>org.opendaylight.yangtools</groupId>
+        <artifactId>yang-maven-plugin</artifactId>
+        <executions>
+          <execution>
+            <goals>
+              <goal>generate-sources</goal>
+            </goals>
+            <configuration>
+              <codeGenerators>
+                <generator>
+                  <codeGeneratorClass>org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl</codeGeneratorClass>
+                  <outputBaseDir>${salGeneratorPath}</outputBaseDir>
+                </generator>
+              </codeGenerators>
+              <inspectDependencies>true</inspectDependencies>
+            </configuration>
+          </execution>
+        </executions>
+    </plugin>
       <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/main/yang/opendaylight-sal-test-store.yang b/opendaylight/md-sal/sal-binding-dom-it/src/main/yang/opendaylight-sal-test-store.yang
new file mode 100644 (file)
index 0000000..1d3f6df
--- /dev/null
@@ -0,0 +1,49 @@
+module opendaylight-sal-test-store {
+       yang-version 1;
+    namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:test:store";
+    prefix "binding-impl";
+
+    description
+        "Regression Test model for data store";
+
+    revision "2014-04-22" {
+        description
+            "Initial revision";
+    }
+
+    grouping name-value {
+        leaf name {
+            type string;
+        }
+        leaf value {
+            type string;
+        }
+    }
+
+    container lists {
+        config false;
+        container unordered-container {
+            list unordered-list {
+                key "name";
+                uses name-value;
+            }
+        }
+        container ordered-container {
+            list ordered-list {
+                ordered-by user;
+                key "name";
+                uses name-value;
+            }
+        }
+        container unkeyed-container {
+            /*
+                Following list nodes not specify key, which
+                makes impossible to uniquely identify list items
+                over time.
+            */
+            list unkeyed-list {
+                uses name-value;
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ListProcessingAndOrderingTest.java b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ListProcessingAndOrderingTest.java
new file mode 100644 (file)
index 0000000..0925e9b
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * 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.md.sal.binding.data;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertTrue;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+
+import org.junit.Test;
+import org.opendaylight.controller.md.sal.common.api.TransactionStatus;
+import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.dom.api.DOMDataReadTransaction;
+import org.opendaylight.controller.sal.binding.api.data.DataModificationTransaction;
+import org.opendaylight.controller.sal.binding.test.AbstractDataServiceTest;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.Lists;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.OrderedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.UnkeyedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.UnkeyedContainerBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.UnorderedContainer;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.ordered.container.OrderedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.ordered.container.OrderedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.ordered.container.OrderedListKey;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.unkeyed.container.UnkeyedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.unkeyed.container.UnkeyedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.unordered.container.UnorderedList;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.unordered.container.UnorderedListBuilder;
+import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.md.sal.test.store.rev140422.lists.unordered.container.UnorderedListKey;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.data.api.CompositeNode;
+import org.opendaylight.yangtools.yang.data.api.Node;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+
+/*
+ * 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
+ */
+public class ListProcessingAndOrderingTest extends AbstractDataServiceTest {
+
+    private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier DOM_UNORDERED_LIST_PATH = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+            .builder(Lists.QNAME).node(UnorderedContainer.QNAME).node(UnorderedList.QNAME).build();
+
+    private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier DOM_ORDERED_LIST_PATH = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+            .builder(Lists.QNAME).node(OrderedContainer.QNAME).node(OrderedList.QNAME).build();
+
+
+    private static final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier DOM_UNKEYED_LIST_PATH = org.opendaylight.yangtools.yang.data.api.InstanceIdentifier
+            .builder(Lists.QNAME).node(UnkeyedContainer.QNAME).node(UnkeyedList.QNAME).build();
+
+    private static final InstanceIdentifier<UnorderedContainer> UNORDERED_CONTAINER_PATH = InstanceIdentifier.builder(Lists.class).child(UnorderedContainer.class).build();
+    private static final InstanceIdentifier<OrderedContainer> ORDERED_CONTAINER_PATH = InstanceIdentifier.builder(Lists.class).child(OrderedContainer.class).build();
+    private static final InstanceIdentifier<UnkeyedContainer> UNKEYED_CONTAINER_PATH = InstanceIdentifier.builder(Lists.class).child(UnkeyedContainer.class).build();
+
+    private static final UnorderedListKey UNORDERED_FOO_KEY = new UnorderedListKey("foo");
+    private static final UnorderedListKey UNORDERED_BAR_KEY = new UnorderedListKey("bar");
+
+    private static final InstanceIdentifier<UnorderedList> UNORDERED_FOO_PATH = InstanceIdentifier.builder(UNORDERED_CONTAINER_PATH).child(UnorderedList.class,UNORDERED_FOO_KEY).build();
+    private static final InstanceIdentifier<UnorderedList> UNORDERED_BAR_PATH = InstanceIdentifier.builder(UNORDERED_CONTAINER_PATH).child(UnorderedList.class,UNORDERED_BAR_KEY).build();
+
+    private static final OrderedListKey ORDERED_FOO_KEY = new OrderedListKey("foo");
+    private static final OrderedListKey ORDERED_BAR_KEY = new OrderedListKey("bar");
+    private static final InstanceIdentifier<OrderedList> ORDERED_FOO_PATH = InstanceIdentifier.builder(ORDERED_CONTAINER_PATH).child(OrderedList.class,ORDERED_FOO_KEY).build();
+    private static final InstanceIdentifier<OrderedList> ORDERED_BAR_PATH = InstanceIdentifier.builder(ORDERED_CONTAINER_PATH).child(OrderedList.class,ORDERED_BAR_KEY).build();
+
+
+    @Test
+    public void unorderedListReadWriteTest() throws InterruptedException, ExecutionException {
+        DataModificationTransaction tx = baDataService.beginTransaction();
+
+        tx.putOperationalData(UNORDERED_FOO_PATH, createUnordered("foo"));
+        tx.putOperationalData(UNORDERED_BAR_PATH, createUnordered("bar"));
+        assertedCommit(tx);
+
+        NormalizedNode<?, ?> data = resolveDataAsserted(DOM_UNORDERED_LIST_PATH);
+        assertTrue(data instanceof MapNode);
+        assertFalse(data instanceof OrderedMapNode);
+
+        assertXmlRepresentation(UNORDERED_CONTAINER_PATH, "foo","bar");
+    }
+
+
+
+    @Test
+    public void orderedListReadWriteTest() throws InterruptedException, ExecutionException {
+        DataModificationTransaction tx = baDataService.beginTransaction();
+
+        tx.putOperationalData(ORDERED_FOO_PATH, createOrdered("foo"));
+        tx.putOperationalData(ORDERED_BAR_PATH, createOrdered("bar"));
+        assertedCommit(tx);
+        NormalizedNode<?, ?> data = resolveDataAsserted(DOM_ORDERED_LIST_PATH);
+        assertTrue(data instanceof MapNode);
+        assertTrue(data instanceof OrderedMapNode);
+
+        assertXmlRepresentation(ORDERED_CONTAINER_PATH, "foo","bar");
+
+    }
+
+
+
+    @Test
+    public void unkeyedListReadWriteTest() throws InterruptedException, ExecutionException {
+        DataModificationTransaction tx = baDataService.beginTransaction();
+
+        ImmutableList<UnkeyedList> unkeyedItems= ImmutableList.<UnkeyedList>builder()
+                .add(createUnkeyed("foo"))
+                .add(createUnkeyed("bar"))
+                .build();
+
+        tx.putOperationalData(UNKEYED_CONTAINER_PATH, new UnkeyedContainerBuilder().setUnkeyedList(unkeyedItems).build());
+        assertedCommit(tx);
+        NormalizedNode<?, ?> data = resolveDataAsserted(DOM_UNKEYED_LIST_PATH);
+        assertFalse(data instanceof MapNode);
+        assertTrue(data instanceof UnkeyedListNode);
+
+        assertXmlRepresentation(UNKEYED_CONTAINER_PATH, "foo","bar");
+    }
+
+    private NormalizedNode<?, ?> resolveDataAsserted(
+            final org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath) {
+
+        try (DOMDataReadTransaction readTx = testContext.getDomAsyncDataBroker().newReadOnlyTransaction()){
+            ListenableFuture<Optional<NormalizedNode<?, ?>>> data = readTx.read(LogicalDatastoreType.OPERATIONAL, domPath);
+            Optional<NormalizedNode<?, ?>> potential = data.get();
+            assertTrue(potential.isPresent());
+            return potential.get();
+        } catch (InterruptedException | ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static UnorderedList createUnordered(final String name) {
+        return new UnorderedListBuilder().setName(name).setValue(createValue(name)).build();
+    }
+
+    private static OrderedList createOrdered(final String name) {
+        return new OrderedListBuilder().setName(name).setValue(createValue(name)).build();
+    }
+
+    private static UnkeyedList createUnkeyed(final String name) {
+        return new UnkeyedListBuilder().setName(name).setValue(createValue(name)).build();
+    }
+
+    private static String createValue(final String name) {
+        return name + "-" + name.hashCode();
+    }
+
+    private static void assertedCommit(final DataModificationTransaction tx) throws InterruptedException, ExecutionException {
+        RpcResult<TransactionStatus> result = tx.commit().get();
+        assertTrue(result.isSuccessful());
+        assertEquals(TransactionStatus.COMMITED,result.getResult());
+    }
+
+    @SuppressWarnings("deprecation")
+    private void assertXmlRepresentation(final InstanceIdentifier<?> containerPath, final String... childNameValues) {
+
+        org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = testContext.getBindingToDomMappingService().toDataDom(containerPath);
+        CompositeNode compositeNode = testContext.getDomDataBroker().readOperationalData(domPath);
+        assertNotNull(compositeNode);
+
+        Set<String> childValues = new HashSet<>();
+        Collections.addAll(childValues, childNameValues);
+
+        for(Node<?> child : compositeNode.getChildren()) {
+            assertTrue(child instanceof CompositeNode);
+            CompositeNode compChild = (CompositeNode) child;
+            String nameLeafValue = (String) compChild.getSimpleNodesByName("name").get(0).getValue();
+            String valueLeafValue = (String) compChild.getSimpleNodesByName("value").get(0).getValue();
+
+            assertEquals(createValue(nameLeafValue), valueLeafValue);
+            childValues.remove(nameLeafValue);
+        }
+        assertTrue(childValues.isEmpty());
+    }
+
+}
index ae9b17b..a36d984 100644 (file)
@@ -30,7 +30,6 @@ import org.opendaylight.yangtools.yang.data.api.Node;
 import org.opendaylight.yangtools.yang.data.api.SimpleNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
 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;
@@ -110,7 +109,6 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
 
         @Override
         public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
-            // TODO Auto-generated method stub
             return null;
         }
 
@@ -148,16 +146,16 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
         }
     }
 
-    private static abstract class CompositeNodeNormalizationOpertation<T extends PathArgument> extends
+    private static abstract class CompositeNodeNormalizationOperation<T extends PathArgument> extends
             DataNormalizationOperation<T> {
 
-        protected CompositeNodeNormalizationOpertation(final T identifier) {
+        protected CompositeNodeNormalizationOperation(final T identifier) {
             super(identifier);
         }
 
         @SuppressWarnings({ "rawtypes", "unchecked" })
         @Override
-        public final NormalizedNodeContainer<?, ?, ?> normalize(final Node<?> legacyData) {
+        public final NormalizedNode<?, ?> normalize(final Node<?> legacyData) {
             checkArgument(legacyData != null);
             if (!isMixin() && getIdentifier().getNodeType() != null) {
                 checkArgument(getIdentifier().getNodeType().equals(legacyData.getNodeType()),
@@ -197,7 +195,7 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
                     builder.addChild(childOp.normalize(childLegacy));
                 }
             }
-            return (NormalizedNodeContainer<?, ?, ?>) builder.build();
+            return builder.build();
         }
 
         @SuppressWarnings("rawtypes")
@@ -206,7 +204,7 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
     }
 
     private static abstract class DataContainerNormalizationOperation<T extends PathArgument> extends
-            CompositeNodeNormalizationOpertation<T> {
+            CompositeNodeNormalizationOperation<T> {
 
         private final DataNodeContainer schema;
         private final Map<QName, DataNormalizationOperation<?>> byQName;
@@ -295,6 +293,24 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
         }
     }
 
+    private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
+
+        protected UnkeyedListItemNormalization(final ListSchemaNode schema) {
+            super(new NodeIdentifier(schema.getQName()), schema);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.unkeyedListEntryBuilder().withNodeIdentifier((NodeIdentifier) currentArg).build();
+        }
+
+    }
+
     private static final class ContainerNormalization extends DataContainerNormalizationOperation<NodeIdentifier> {
 
         protected ContainerNormalization(final ContainerSchemaNode schema) {
@@ -314,7 +330,7 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
     }
 
     private static abstract class MixinNormalizationOp<T extends PathArgument> extends
-            CompositeNodeNormalizationOpertation<T> {
+            CompositeNodeNormalizationOperation<T> {
 
         protected MixinNormalizationOp(final T identifier) {
             super(identifier);
@@ -327,11 +343,30 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
 
     }
 
-    private static final class LeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+    private static final class OrderedLeafListMixinNormalization extends UnorderedLeafListMixinNormalization {
+
+
+        public OrderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
+            super(potential);
+        }
+
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.orderedLeafSetBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+    }
+
+    private static class UnorderedLeafListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
 
         private final DataNormalizationOperation<?> innerOp;
 
-        public LeafListMixinNormalization(final LeafListSchemaNode potential) {
+        public UnorderedLeafListMixinNormalization(final LeafListSchemaNode potential) {
             super(new NodeIdentifier(potential.getQName()));
             innerOp = new LeafListEntryNormalization(potential);
         }
@@ -415,11 +450,11 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
 
     }
 
-    private static final class ListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+    private static class UnorderedMapMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
 
         private final ListItemNormalization innerNode;
 
-        public ListMixinNormalization(final ListSchemaNode list) {
+        public UnorderedMapMixinNormalization(final ListSchemaNode list) {
             super(new NodeIdentifier(list.getQName()));
             this.innerNode = new ListItemNormalization(new NodeIdentifierWithPredicates(list.getQName(),
                     Collections.<QName, Object> emptyMap()), list);
@@ -454,6 +489,64 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
 
     }
 
+
+    private static class UnkeyedListMixinNormalization extends MixinNormalizationOp<NodeIdentifier> {
+
+        private final UnkeyedListItemNormalization innerNode;
+
+        public UnkeyedListMixinNormalization(final ListSchemaNode list) {
+            super(new NodeIdentifier(list.getQName()));
+            this.innerNode = new UnkeyedListItemNormalization(list);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.unkeyedListBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final PathArgument child) {
+            if (child.getNodeType().equals(getIdentifier().getNodeType())) {
+                return innerNode;
+            }
+            return null;
+        }
+
+        @Override
+        public DataNormalizationOperation<?> getChild(final QName child) {
+            if (getIdentifier().getNodeType().equals(child)) {
+                return innerNode;
+            }
+            return null;
+        }
+
+    }
+
+    private static final class OrderedMapMixinNormalization extends UnorderedMapMixinNormalization {
+
+        public OrderedMapMixinNormalization(final ListSchemaNode list) {
+            super(list);
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final CompositeNode compositeNode) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier());
+        }
+
+        @Override
+        public NormalizedNode<?, ?> createDefault(final PathArgument currentArg) {
+            return Builders.orderedMapBuilder().withNodeIdentifier(getIdentifier()).build();
+        }
+
+    }
+
     private static class ChoiceNodeNormalization extends MixinNormalizationOp<NodeIdentifier> {
 
         private final ImmutableMap<QName, DataNormalizationOperation<?>> byQName;
@@ -569,17 +662,37 @@ public abstract class DataNormalizationOperation<T extends PathArgument> impleme
         if (potential instanceof ContainerSchemaNode) {
             return new ContainerNormalization((ContainerSchemaNode) potential);
         } else if (potential instanceof ListSchemaNode) {
-            return new ListMixinNormalization((ListSchemaNode) potential);
+
+            return fromListSchemaNode((ListSchemaNode) potential);
         } else if (potential instanceof LeafSchemaNode) {
             return new LeafNormalization(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 fromLeafListSchemaNode((LeafListSchemaNode) potential);
         }
         return null;
     }
 
+    private static DataNormalizationOperation<?> fromListSchemaNode(final ListSchemaNode potential) {
+        List<QName> keyDefinition = potential.getKeyDefinition();
+        if(keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListMixinNormalization(potential);
+        }
+        if(potential.isUserOrdered()) {
+            return new OrderedMapMixinNormalization(potential);
+        }
+        return new UnorderedMapMixinNormalization(potential);
+    }
+
+    private static DataNormalizationOperation<?> fromLeafListSchemaNode(final LeafListSchemaNode potential) {
+        if(potential.isUserOrdered()) {
+            return new OrderedLeafListMixinNormalization(potential);
+        }
+        return new UnorderedLeafListMixinNormalization(potential);
+    }
+
+
     public static DataNormalizationOperation<?> from(final SchemaContext ctx) {
         return new ContainerNormalization(ctx);
     }
index 8fb6ff3..33a9869 100644 (file)
@@ -25,13 +25,14 @@ import org.opendaylight.yangtools.yang.data.api.schema.DataContainerNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MixinNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
 import org.opendaylight.yangtools.yang.data.impl.ImmutableCompositeNode;
 import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl;
 import org.opendaylight.yangtools.yang.data.impl.util.CompositeNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 
 import com.google.common.base.Preconditions;
-import com.google.common.base.Predicate;
+import com.google.common.base.Predicates;
 import com.google.common.collect.FluentIterable;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
@@ -165,6 +166,8 @@ public class DataNormalizer {
         for (NormalizedNode<?, ?> child : node.getValue()) {
             if (child instanceof MixinNode && child instanceof NormalizedNodeContainer<?, ?, ?>) {
                 builder.addAll(toLegacyNodesFromMixin((NormalizedNodeContainer) child));
+            } else if( child instanceof UnkeyedListNode) {
+                builder.addAll(toLegacyNodesFromUnkeyedList((UnkeyedListNode) child));
             } else {
                 addToBuilder(builder, toLegacy(child));
             }
@@ -172,6 +175,14 @@ public class DataNormalizer {
         return builder.toInstance();
     }
 
+    private static Iterable<? extends Node<?>> toLegacyNodesFromUnkeyedList(final UnkeyedListNode mixin) {
+        ArrayList<Node<?>> ret = new ArrayList<>();
+        for (NormalizedNode<?, ?> child : mixin.getValue()) {
+            ret.add(toLegacy(child));
+        }
+        return FluentIterable.from(ret).filter(Predicates.notNull());
+    }
+
     private static void addToBuilder(final CompositeNodeBuilder<ImmutableCompositeNode> builder, final Node<?> legacy) {
         if (legacy != null) {
             builder.add(legacy);
@@ -189,13 +200,7 @@ public class DataNormalizer {
                 ret.add(toLegacy(child));
             }
         }
-        return FluentIterable.from(ret).filter(new Predicate<Node<?>>() {
-
-            @Override
-            public boolean apply(final Node<?> input) {
-                return input != null;
-            }
-        });
+        return FluentIterable.from(ret).filter(Predicates.notNull());
     }
 
     public DataNormalizationOperation<?> getRootOperation() {
index dd7eb3f..b9b1ab0 100644 (file)
@@ -2,6 +2,7 @@ package org.opendaylight.controller.md.sal.dom.store.impl;
 
 import static com.google.common.base.Preconditions.checkArgument;
 
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ExecutionException;
@@ -10,6 +11,7 @@ import org.opendaylight.controller.md.sal.dom.store.impl.tree.ModificationType;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.NodeModification;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreMetadataNode;
 import org.opendaylight.controller.md.sal.dom.store.impl.tree.StoreNodeCompositeBuilder;
+import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.AugmentationIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifier;
 import org.opendaylight.yangtools.yang.data.api.InstanceIdentifier.NodeIdentifierWithPredicates;
@@ -24,6 +26,8 @@ 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.api.schema.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
@@ -32,6 +36,8 @@ import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableCo
 import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetNodeBuilder;
 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.data.impl.schema.builder.impl.ImmutableOrderedMapNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListEntryNodeBuilder;
 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
@@ -60,7 +66,7 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
         if (schemaNode instanceof ContainerSchemaNode) {
             return new ContainerModificationStrategy((ContainerSchemaNode) schemaNode);
         } else if (schemaNode instanceof ListSchemaNode) {
-            return new ListMapModificationStrategy((ListSchemaNode) schemaNode);
+            return fromListSchemaNode((ListSchemaNode) schemaNode);
         } else if (schemaNode instanceof ChoiceNode) {
             return new ChoiceModificationStrategy((ChoiceNode) schemaNode);
         } else if (schemaNode instanceof LeafListSchemaNode) {
@@ -71,26 +77,36 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
         throw new IllegalArgumentException("Not supported schema node type for " + schemaNode.getClass());
     }
 
+    private static SchemaAwareApplyOperation fromListSchemaNode(final ListSchemaNode schemaNode) {
+        List<QName> keyDefinition = schemaNode.getKeyDefinition();
+        if (keyDefinition == null || keyDefinition.isEmpty()) {
+            return new UnkeyedListModificationStrategy(schemaNode);
+        }
+        if (schemaNode.isUserOrdered()) {
+            return new OrderedMapModificationStrategy(schemaNode);
+        }
+
+        return new UnorderedMapModificationStrategy(schemaNode);
+    }
+
     public static SchemaAwareApplyOperation from(final DataNodeContainer resolvedTree,
             final AugmentationTarget augSchemas, final AugmentationIdentifier identifier) {
         AugmentationSchema augSchema = null;
-        allAugments : for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
+        allAugments: for (AugmentationSchema potential : augSchemas.getAvailableAugmentations()) {
             boolean containsAll = true;
-            for(DataSchemaNode child : potential.getChildNodes()) {
-                if(identifier.getPossibleChildNames().contains(child.getQName())) {
+            for (DataSchemaNode child : potential.getChildNodes()) {
+                if (identifier.getPossibleChildNames().contains(child.getQName())) {
                     augSchema = potential;
                     break allAugments;
                 }
             }
         }
-        if(augSchema != null) {
-            return new AugmentationModificationStrategy(augSchema,resolvedTree);
+        if (augSchema != null) {
+            return new AugmentationModificationStrategy(augSchema, resolvedTree);
         }
         return null;
     }
 
-
-
     protected final ModificationApplyOperation resolveChildOperation(final PathArgument child) {
         Optional<ModificationApplyOperation> potential = getChild(child);
         checkArgument(potential.isPresent(), "Operation for child %s is not defined.", child);
@@ -152,10 +168,12 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
 
         switch (modification.getModificationType()) {
         case DELETE:
-            return modification.storeSnapshot(Optional.<StoreMetadataNode>absent());
+            return modification.storeSnapshot(Optional.<StoreMetadataNode> absent());
         case SUBTREE_MODIFIED:
-            Preconditions.checkArgument(currentMeta.isPresent(),"Metadata not available for modification",modification);
-            return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(), subtreeVersion)));
+            Preconditions.checkArgument(currentMeta.isPresent(), "Metadata not available for modification",
+                    modification);
+            return modification.storeSnapshot(Optional.of(applySubtreeChange(modification, currentMeta.get(),
+                    subtreeVersion)));
         case WRITE:
             return modification.storeSnapshot(Optional.of(applyWrite(modification, currentMeta, subtreeVersion)));
         case UNMODIFIED:
@@ -374,8 +392,8 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
             NormalizedNodeContainerModificationStrategy {
 
         private final T schema;
-        private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder().build(
-                CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
+        private final LoadingCache<PathArgument, ModificationApplyOperation> childCache = CacheBuilder.newBuilder()
+                .build(CacheLoader.from(new Function<PathArgument, ModificationApplyOperation>() {
 
                     @Override
                     public ModificationApplyOperation apply(final PathArgument identifier) {
@@ -438,6 +456,22 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
 
     }
 
+    public static class UnkeyedListItemModificationStrategy extends
+            DataNodeContainerModificationStrategy<ListSchemaNode> {
+
+        public UnkeyedListItemModificationStrategy(final ListSchemaNode schemaNode) {
+            super(schemaNode, UnkeyedListEntryNode.class);
+        }
+
+        @Override
+        @SuppressWarnings("rawtypes")
+        protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
+            checkArgument(identifier instanceof NodeIdentifier);
+            return ImmutableUnkeyedListEntryNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+    }
+
     public static class AugmentationModificationStrategy extends
             DataNodeContainerModificationStrategy<AugmentationSchema> {
 
@@ -447,7 +481,6 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
 
         }
 
-
         @Override
         protected DataContainerNodeBuilder createBuilder(final PathArgument identifier) {
             return Builders.augmentationBuilder().withNodeIdentifier((AugmentationIdentifier) identifier);
@@ -458,17 +491,17 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
 
         private final ChoiceNode schema;
-        private final Map<PathArgument,ModificationApplyOperation> childNodes;
+        private final Map<PathArgument, ModificationApplyOperation> childNodes;
 
         public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
             super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
             this.schema = schemaNode;
             ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
 
-            for(ChoiceCaseNode caze : schemaNode.getCases()) {
-                for(DataSchemaNode cazeChild : caze.getChildNodes()) {
+            for (ChoiceCaseNode caze : schemaNode.getCases()) {
+                for (DataSchemaNode cazeChild : caze.getChildNodes()) {
                     SchemaAwareApplyOperation childNode = from(cazeChild);
-                    child.put(new NodeIdentifier(cazeChild.getQName()),childNode);
+                    child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
                 }
             }
             childNodes = child.build();
@@ -528,11 +561,54 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
 
     }
 
-    public static class ListMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+    public static class UnkeyedListModificationStrategy extends SchemaAwareApplyOperation {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        protected UnkeyedListModificationStrategy(final ListSchemaNode schema) {
+            entryStrategy = Optional.<ModificationApplyOperation> of(new UnkeyedListItemModificationStrategy(schema));
+        }
+
+
+
+        @Override
+        protected StoreMetadataNode applySubtreeChange(final NodeModification modification,
+                final StoreMetadataNode currentMeta, final UnsignedLong subtreeVersion) {
+            throw new UnsupportedOperationException("UnkeyedList does not support subtree change.");
+        }
+
+        @Override
+        protected StoreMetadataNode applyWrite(final NodeModification modification,
+                final Optional<StoreMetadataNode> currentMeta, final UnsignedLong subtreeVersion) {
+            return StoreMetadataNode.createRecursively(modification.getWritenValue(), subtreeVersion);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+            if (child instanceof NodeIdentifier) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+        @Override
+        protected void verifyWritenStructure(final NormalizedNode<?, ?> writenValue) {
+
+        }
+
+        @Override
+        protected boolean isSubtreeModificationApplicable(final NodeModification modification,
+                final Optional<StoreMetadataNode> current) {
+            return false;
+        }
+
+    }
+
+    public static class UnorderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
 
         private final Optional<ModificationApplyOperation> entryStrategy;
 
-        protected ListMapModificationStrategy(final ListSchemaNode schema) {
+        protected UnorderedMapModificationStrategy(final ListSchemaNode schema) {
             super(MapNode.class);
             entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
         }
@@ -553,7 +629,36 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper
 
         @Override
         public String toString() {
-            return "ListMapModificationStrategy [entry=" + entryStrategy + "]";
+            return "UnorderedMapModificationStrategy [entry=" + entryStrategy + "]";
+        }
+    }
+
+    public static class OrderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy {
+
+        private final Optional<ModificationApplyOperation> entryStrategy;
+
+        protected OrderedMapModificationStrategy(final ListSchemaNode schema) {
+            super(OrderedMapNode.class);
+            entryStrategy = Optional.<ModificationApplyOperation> of(new ListEntryModificationStrategy(schema));
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) {
+            return ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier);
+        }
+
+        @Override
+        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
+            if (identifier instanceof NodeIdentifierWithPredicates) {
+                return entryStrategy;
+            }
+            return Optional.absent();
+        }
+
+        @Override
+        public String toString() {
+            return "OrderedMapModificationStrategy [entry=" + entryStrategy + "]";
         }
     }