From 866769acecbabd1c8b24f3f3887349029454ba54 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Tue, 22 Apr 2014 14:54:04 +0200 Subject: [PATCH] BUG-509, BUG-808: Added datastore support for lists without keys. 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 Signed-off-by: Robert Varga --- .../binding/test/util/BindingTestContext.java | 4 + .../md-sal/sal-binding-dom-it/pom.xml | 28 ++- .../yang/opendaylight-sal-test-store.yang | 49 +++++ .../data/ListProcessingAndOrderingTest.java | 196 ++++++++++++++++++ .../compat/DataNormalizationOperation.java | 141 +++++++++++-- .../impl/util/compat/DataNormalizer.java | 21 +- .../store/impl/SchemaAwareApplyOperation.java | 147 +++++++++++-- 7 files changed, 539 insertions(+), 47 deletions(-) create mode 100644 opendaylight/md-sal/sal-binding-dom-it/src/main/yang/opendaylight-sal-test-store.yang create mode 100644 opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ListProcessingAndOrderingTest.java diff --git a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java index d6d87bac84..9cdf70322c 100644 --- a/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java +++ b/opendaylight/md-sal/sal-binding-broker/src/test/java/org/opendaylight/controller/sal/binding/test/util/BindingTestContext.java @@ -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; diff --git a/opendaylight/md-sal/sal-binding-dom-it/pom.xml b/opendaylight/md-sal/sal-binding-dom-it/pom.xml index 099b77c956..21fa207d78 100644 --- a/opendaylight/md-sal/sal-binding-dom-it/pom.xml +++ b/opendaylight/md-sal/sal-binding-dom-it/pom.xml @@ -25,6 +25,10 @@ test-jar test + + org.opendaylight.yangtools + yang-binding + org.opendaylight.controller.model model-flow-management @@ -50,10 +54,26 @@ - - org.eclipse.xtend - xtend-maven-plugin - + + org.opendaylight.yangtools + yang-maven-plugin + + + + generate-sources + + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + ${salGeneratorPath} + + + true + + + + org.jacoco jacoco-maven-plugin 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 index 0000000000..1d3f6df840 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-dom-it/src/main/yang/opendaylight-sal-test-store.yang @@ -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 index 0000000000..0925e9bff4 --- /dev/null +++ b/opendaylight/md-sal/sal-binding-dom-it/src/test/java/org/opendaylight/controller/md/sal/binding/data/ListProcessingAndOrderingTest.java @@ -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 UNORDERED_CONTAINER_PATH = InstanceIdentifier.builder(Lists.class).child(UnorderedContainer.class).build(); + private static final InstanceIdentifier ORDERED_CONTAINER_PATH = InstanceIdentifier.builder(Lists.class).child(OrderedContainer.class).build(); + private static final InstanceIdentifier 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 UNORDERED_FOO_PATH = InstanceIdentifier.builder(UNORDERED_CONTAINER_PATH).child(UnorderedList.class,UNORDERED_FOO_KEY).build(); + private static final InstanceIdentifier 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 ORDERED_FOO_PATH = InstanceIdentifier.builder(ORDERED_CONTAINER_PATH).child(OrderedList.class,ORDERED_FOO_KEY).build(); + private static final InstanceIdentifier 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 unkeyedItems= ImmutableList.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>> data = readTx.read(LogicalDatastoreType.OPERATIONAL, domPath); + Optional> 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 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 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()); + } + +} diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java index ae9b17bde4..a36d984fa7 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizationOperation.java @@ -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 impleme @Override public NormalizedNode createDefault(final PathArgument currentArg) { - // TODO Auto-generated method stub return null; } @@ -148,16 +146,16 @@ public abstract class DataNormalizationOperation impleme } } - private static abstract class CompositeNodeNormalizationOpertation extends + private static abstract class CompositeNodeNormalizationOperation extends DataNormalizationOperation { - 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 impleme builder.addChild(childOp.normalize(childLegacy)); } } - return (NormalizedNodeContainer) builder.build(); + return builder.build(); } @SuppressWarnings("rawtypes") @@ -206,7 +204,7 @@ public abstract class DataNormalizationOperation impleme } private static abstract class DataContainerNormalizationOperation extends - CompositeNodeNormalizationOpertation { + CompositeNodeNormalizationOperation { private final DataNodeContainer schema; private final Map> byQName; @@ -295,6 +293,24 @@ public abstract class DataNormalizationOperation impleme } } + private static final class UnkeyedListItemNormalization extends DataContainerNormalizationOperation { + + 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 { protected ContainerNormalization(final ContainerSchemaNode schema) { @@ -314,7 +330,7 @@ public abstract class DataNormalizationOperation impleme } private static abstract class MixinNormalizationOp extends - CompositeNodeNormalizationOpertation { + CompositeNodeNormalizationOperation { protected MixinNormalizationOp(final T identifier) { super(identifier); @@ -327,11 +343,30 @@ public abstract class DataNormalizationOperation impleme } - private static final class LeafListMixinNormalization extends MixinNormalizationOp { + + 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 { 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 impleme } - private static final class ListMixinNormalization extends MixinNormalizationOp { + private static class UnorderedMapMixinNormalization extends MixinNormalizationOp { 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. emptyMap()), list); @@ -454,6 +489,64 @@ public abstract class DataNormalizationOperation impleme } + + private static class UnkeyedListMixinNormalization extends MixinNormalizationOp { + + 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 { private final ImmutableMap> byQName; @@ -569,17 +662,37 @@ public abstract class DataNormalizationOperation 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 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); } diff --git a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java index 8fb6ff38a2..33a9869a6b 100644 --- a/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java +++ b/opendaylight/md-sal/sal-common-impl/src/main/java/org/opendaylight/controller/md/sal/common/impl/util/compat/DataNormalizer.java @@ -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> toLegacyNodesFromUnkeyedList(final UnkeyedListNode mixin) { + ArrayList> 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 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>() { - - @Override - public boolean apply(final Node input) { - return input != null; - } - }); + return FluentIterable.from(ret).filter(Predicates.notNull()); } public DataNormalizationOperation getRootOperation() { diff --git a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java index dd7eb3f71b..b9b1ab035e 100644 --- a/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java +++ b/opendaylight/md-sal/sal-dom-broker/src/main/java/org/opendaylight/controller/md/sal/dom/store/impl/SchemaAwareApplyOperation.java @@ -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 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 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.absent()); + return modification.storeSnapshot(Optional. 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 childCache = CacheBuilder.newBuilder().build( - CacheLoader.from(new Function() { + private final LoadingCache childCache = CacheBuilder.newBuilder() + .build(CacheLoader.from(new Function() { @Override public ModificationApplyOperation apply(final PathArgument identifier) { @@ -438,6 +456,22 @@ public abstract class SchemaAwareApplyOperation implements ModificationApplyOper } + public static class UnkeyedListItemModificationStrategy extends + DataNodeContainerModificationStrategy { + + 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 { @@ -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 childNodes; + private final Map childNodes; public ChoiceModificationStrategy(final ChoiceNode schemaNode) { super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class); this.schema = schemaNode; ImmutableMap.Builder 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 entryStrategy; + + protected UnkeyedListModificationStrategy(final ListSchemaNode schema) { + entryStrategy = Optional. 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 currentMeta, final UnsignedLong subtreeVersion) { + return StoreMetadataNode.createRecursively(modification.getWritenValue(), subtreeVersion); + } + + @Override + public Optional 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 current) { + return false; + } + + } + + public static class UnorderedMapModificationStrategy extends NormalizedNodeContainerModificationStrategy { private final Optional entryStrategy; - protected ListMapModificationStrategy(final ListSchemaNode schema) { + protected UnorderedMapModificationStrategy(final ListSchemaNode schema) { super(MapNode.class); entryStrategy = Optional. 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 entryStrategy; + + protected OrderedMapModificationStrategy(final ListSchemaNode schema) { + super(OrderedMapNode.class); + entryStrategy = Optional. of(new ListEntryModificationStrategy(schema)); + } + + @SuppressWarnings("rawtypes") + @Override + protected NormalizedNodeContainerBuilder createBuilder(final PathArgument identifier) { + return ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier((NodeIdentifier) identifier); + } + + @Override + public Optional getChild(final PathArgument identifier) { + if (identifier instanceof NodeIdentifierWithPredicates) { + return entryStrategy; + } + return Optional.absent(); + } + + @Override + public String toString() { + return "OrderedMapModificationStrategy [entry=" + entryStrategy + "]"; } } -- 2.36.6