From: Robert Varga Date: Tue, 2 Sep 2014 12:34:20 +0000 (+0000) Subject: Merge "Added tests for yang.model.util" X-Git-Tag: release/helium~93 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=300aed51915840a41a1483980ff7d045bde905cb;hp=a71ab3b0e3e59e938bae3b71f614e08b335e641e;p=yangtools.git Merge "Added tests for yang.model.util" --- diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml new file mode 100644 index 0000000000..e80bfee9f9 --- /dev/null +++ b/benchmarks/pom.xml @@ -0,0 +1,59 @@ + + + + org.opendaylight.yangtools + yangtools-parent + 0.6.2-SNAPSHOT + ../common/parent + + 4.0.0 + + org.opendaylight.yangtools + benchmarks + + + 0.6.2-SNAPSHOT + 0.6.2-SNAPSHOT + 1.7 + 1.7 + 0.9.7 + + + + + ${project.groupId} + yang-data-impl + ${yangtools.version} + + + ${project.groupId} + yang-parser-impl + ${yangtools.version} + + + org.openjdk.jmh + jmh-core + ${jmh.version} + + + org.openjdk.jmh + jmh-generator-annprocess + ${jmh.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.source.version} + ${java.source.version} + + + + + diff --git a/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java new file mode 100644 index 0000000000..e8cf728b45 --- /dev/null +++ b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java @@ -0,0 +1,44 @@ +package org.opendaylight.yangtools.yang.data.impl.tree; + +import java.io.InputStream; +import java.util.Collections; +import java.util.Set; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; + +/** + * @author Lukas Sedlak + */ +public class BenchmarkModel { + + public static final QName TEST_QNAME = QName.create("urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test", "2014-03-13", + "test"); + public static final QName OUTER_LIST_QNAME = QName.create(TEST_QNAME, "outer-list"); + public static final QName INNER_LIST_QNAME = QName.create(TEST_QNAME, "inner-list"); + public static final QName OUTER_CHOICE_QNAME = QName.create(TEST_QNAME, "outer-choice"); + public static final QName ID_QNAME = QName.create(TEST_QNAME, "id"); + public static final QName NAME_QNAME = QName.create(TEST_QNAME, "name"); + public static final QName VALUE_QNAME = QName.create(TEST_QNAME, "value"); + private static final String DATASTORE_TEST_YANG = "/odl-datastore-test.yang"; + + public static final YangInstanceIdentifier TEST_PATH = YangInstanceIdentifier.of(TEST_QNAME); + public static final YangInstanceIdentifier OUTER_LIST_PATH = YangInstanceIdentifier.builder(TEST_PATH).node(OUTER_LIST_QNAME).build(); + + public static final InputStream getDatastoreBenchmarkInputStream() { + return getInputStream(DATASTORE_TEST_YANG); + } + + private static InputStream getInputStream(final String resourceName) { + return BenchmarkModel.class.getResourceAsStream(resourceName); + } + + public static SchemaContext createTestContext() { + YangParserImpl parser = new YangParserImpl(); + Set modules = parser.parseYangModelsFromStreams(Collections.singletonList( + getDatastoreBenchmarkInputStream())); + return parser.resolveSchemaContext(modules); + } +} diff --git a/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java new file mode 100644 index 0000000000..306c14dc07 --- /dev/null +++ b/benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2013 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.tree; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +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.tree.*; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.openjdk.jmh.annotations.*; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +/** + * Benchmarking of InMemoryDataTree performance. + * + * JMH is used for microbenchmarking. + * + * @author Lukas Sedlak + * + * @see JMH + */ +@State(Scope.Thread) +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.MILLISECONDS) +public class InMemoryDataTreeBenchmark { + + private static final int OUTER_LIST_100K = 100000; + private static final int OUTER_LIST_50K = 50000; + private static final int OUTER_LIST_10K = 10000; + + private static final YangInstanceIdentifier[] OUTER_LIST_100K_PATHS = initOuterListPaths(OUTER_LIST_100K); + private static final YangInstanceIdentifier[] OUTER_LIST_50K_PATHS = initOuterListPaths(OUTER_LIST_50K); + private static final YangInstanceIdentifier[] OUTER_LIST_10K_PATHS = initOuterListPaths(OUTER_LIST_10K); + + private static YangInstanceIdentifier[] initOuterListPaths(final int outerListPathsCount) { + final YangInstanceIdentifier[] paths = new YangInstanceIdentifier[outerListPathsCount]; + + for (int outerListKey = 0; outerListKey < outerListPathsCount; ++outerListKey) { + paths[outerListKey] = YangInstanceIdentifier.builder(BenchmarkModel.OUTER_LIST_PATH) + .nodeWithKey(BenchmarkModel.OUTER_LIST_QNAME, BenchmarkModel.ID_QNAME, outerListKey) + .build(); + } + return paths; + } + + private static final MapNode ONE_ITEM_INNER_LIST = initInnerListItems(1); + private static final MapNode TWO_ITEM_INNER_LIST = initInnerListItems(2); + private static final MapNode TEN_ITEM_INNER_LIST = initInnerListItems(10); + + private static MapNode initInnerListItems(final int count) { + final CollectionNodeBuilder mapEntryBuilder = ImmutableNodes + .mapNodeBuilder(BenchmarkModel.INNER_LIST_QNAME); + + for (int i = 1; i <= count; ++i) { + mapEntryBuilder + .withChild(ImmutableNodes.mapEntry(BenchmarkModel.INNER_LIST_QNAME, BenchmarkModel.NAME_QNAME, i)); + } + + return mapEntryBuilder.build(); + } + + private static final NormalizedNode[] OUTER_LIST_ONE_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_100K, ONE_ITEM_INNER_LIST); + private static final NormalizedNode[] OUTER_LIST_TWO_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_50K, TWO_ITEM_INNER_LIST); + private static final NormalizedNode[] OUTER_LIST_TEN_ITEM_INNER_LIST = initOuterListItems(OUTER_LIST_10K, TEN_ITEM_INNER_LIST); + + private static NormalizedNode[] initOuterListItems(int outerListItemsCount, MapNode innerList) { + final NormalizedNode[] outerListItems = new NormalizedNode[outerListItemsCount]; + + for (int i = 0; i < outerListItemsCount; ++i) { + int outerListKey = i; + outerListItems[i] = ImmutableNodes.mapEntryBuilder(BenchmarkModel.OUTER_LIST_QNAME, BenchmarkModel.ID_QNAME, outerListKey) + .withChild(innerList).build(); + } + return outerListItems; + } + + private SchemaContext schemaContext; + private DataTree datastore; + + public static void main(String... args) throws IOException, RunnerException { + Options opt = new OptionsBuilder() + .include(".*" + InMemoryDataTreeBenchmark.class.getSimpleName() + ".*") + .forks(1) + .build(); + + new Runner(opt).run(); + } + + @Setup(Level.Trial) + public void setup() throws DataValidationFailedException { + schemaContext = BenchmarkModel.createTestContext(); + final InMemoryDataTreeFactory factory = InMemoryDataTreeFactory.getInstance(); + datastore = factory.create(); + datastore.setSchemaContext(schemaContext); + final DataTreeSnapshot snapshot = datastore.takeSnapshot(); + initTestNode(snapshot); + } + + @TearDown + public void tearDown() { + schemaContext = null; + datastore = null; + } + + private void initTestNode(final DataTreeSnapshot snapshot) throws DataValidationFailedException { + final DataTreeModification modification = snapshot.newModification(); + final YangInstanceIdentifier testPath = YangInstanceIdentifier.builder(BenchmarkModel.TEST_PATH) + .build(); + + modification.write(testPath, provideOuterListNode()); + datastore.validate(modification); + final DataTreeCandidate candidate = datastore.prepare(modification); + datastore.commit(candidate); + } + + private DataContainerChild provideOuterListNode() { + return ImmutableContainerNodeBuilder + .create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(BenchmarkModel.TEST_QNAME)) + .withChild( + ImmutableNodes.mapNodeBuilder(BenchmarkModel.OUTER_LIST_QNAME) + .build()).build(); + } + + @Benchmark + @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS) + public void singleNodes100KWriteBenchmark() throws Exception { + applyWriteSingleNode(OUTER_LIST_100K); + } + + private void applyWriteSingleNode(final int reps) throws DataValidationFailedException { + final DataTreeSnapshot snapshot = datastore.takeSnapshot(); + final DataTreeModification modification = snapshot.newModification(); + for (int outerListKey = 0; outerListKey < reps; ++outerListKey) { + modification.write(OUTER_LIST_100K_PATHS[outerListKey], OUTER_LIST_ONE_ITEM_INNER_LIST[outerListKey]); + } + datastore.validate(modification); + final DataTreeCandidate candidate = datastore.prepare(modification); + datastore.commit(candidate); + } + + @Benchmark + @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS) + public void twoNodes50KWriteBenchmark() throws Exception { + applyWriteTwoNodes(OUTER_LIST_50K); + } + + private void applyWriteTwoNodes(final int reps) throws DataValidationFailedException { + final DataTreeSnapshot snapshot = datastore.takeSnapshot(); + final DataTreeModification modification = snapshot.newModification(); + for (int outerListKey = 0; outerListKey < reps; ++outerListKey) { + modification.write(OUTER_LIST_50K_PATHS[outerListKey], OUTER_LIST_TWO_ITEM_INNER_LIST[outerListKey]); + } + datastore.validate(modification); + final DataTreeCandidate candidate = datastore.prepare(modification); + datastore.commit(candidate); + } + + @Benchmark + @Warmup(iterations = 10, timeUnit = TimeUnit.MILLISECONDS) + @Measurement(iterations = 20, timeUnit = TimeUnit.MILLISECONDS) + public void tenNodes10KWriteBenchmark() throws Exception { + applyWriteTenNodes(OUTER_LIST_10K); + } + + private void applyWriteTenNodes(final int reps) throws DataValidationFailedException { + final DataTreeSnapshot snapshot = datastore.takeSnapshot(); + final DataTreeModification modification = snapshot.newModification(); + for (int outerListKey = 0; outerListKey < reps; ++outerListKey) { + modification.write(OUTER_LIST_10K_PATHS[outerListKey], OUTER_LIST_TEN_ITEM_INNER_LIST[outerListKey]); + } + datastore.validate(modification); + final DataTreeCandidate candidate = datastore.prepare(modification); + datastore.commit(candidate); + } +} diff --git a/benchmarks/src/main/resources/odl-datastore-test.yang b/benchmarks/src/main/resources/odl-datastore-test.yang new file mode 100644 index 0000000000..730ca17173 --- /dev/null +++ b/benchmarks/src/main/resources/odl-datastore-test.yang @@ -0,0 +1,42 @@ +module odl-datastore-test { + yang-version 1; + namespace "urn:opendaylight:params:xml:ns:yang:controller:md:sal:dom:store:test"; + prefix "store-test"; + + revision "2014-03-13" { + description "Initial revision."; + } + + container test { + list outer-list { + key id; + leaf id { + type int32; + } + 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; + leaf name { + type int32; + } + leaf value { + type string; + } + } + } + } +} \ No newline at end of file diff --git a/code-generator/binding-data-codec/pom.xml b/code-generator/binding-data-codec/pom.xml index 80cfe17b79..b8bf7e11ef 100644 --- a/code-generator/binding-data-codec/pom.xml +++ b/code-generator/binding-data-codec/pom.xml @@ -52,11 +52,12 @@ jsr305 provided - - junit - junit + org.opendaylight.yangtools + binding-test-model + test + org.apache.commons commons-lang3 @@ -65,9 +66,9 @@ com.google.guava guava - - org.eclipse.xtend - org.eclipse.xtend.lib + junit + junit + test diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/api/BindingNormalizedNodeSerializer.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/api/BindingNormalizedNodeSerializer.java index b75afa8b5d..3e10492e10 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/api/BindingNormalizedNodeSerializer.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/api/BindingNormalizedNodeSerializer.java @@ -10,6 +10,9 @@ package org.opendaylight.yangtools.binding.data.codec.api; import java.util.Map; import java.util.Map.Entry; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -18,7 +21,6 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; /** * Serialization service, which provides two-way serialization between * Java Binding Data representation and NormalizedNode representation. - * */ public interface BindingNormalizedNodeSerializer { @@ -28,15 +30,16 @@ public interface BindingNormalizedNodeSerializer { * @param binding Binding Instance Identifier * @return DOM Instance Identifier */ - YangInstanceIdentifier toYangInstanceIdentifier(InstanceIdentifier binding); + YangInstanceIdentifier toYangInstanceIdentifier(@Nonnull InstanceIdentifier binding); /** * Translates supplied YANG Instance Identifier into Binding instance identifier. * * @param dom YANG Instance Identifier - * @return Binding Instance Identifier + * @return Binding Instance Identifier, or null if the instance identifier is not + * representable. */ - InstanceIdentifier fromYangInstanceIdentifier(YangInstanceIdentifier dom); + @Nullable InstanceIdentifier fromYangInstanceIdentifier(@Nonnull YangInstanceIdentifier dom); /** * Translates supplied Binding Instance Identifier and data into NormalizedNode representation. @@ -45,7 +48,7 @@ public interface BindingNormalizedNodeSerializer { * @param data Data object representing data * @return NormalizedNode representation */ - Entry> toNormalizedNode(InstanceIdentifier path, T data); + Entry> toNormalizedNode(InstanceIdentifier path, T data); /** * Translates supplied YANG Instance Identifier and NormalizedNode into Binding data. @@ -54,7 +57,7 @@ public interface BindingNormalizedNodeSerializer { * @param data NormalizedNode representing data * @return DOM Instance Identifier */ - Entry,DataObject> fromNormalizedNode(YangInstanceIdentifier path, NormalizedNode data); + @Nullable Entry,DataObject> fromNormalizedNode(@Nonnull YangInstanceIdentifier path, NormalizedNode data); /** * Returns map view which contains translated set of entries to normalized nodes. diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java index 46fd45960f..49a834710c 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java @@ -18,6 +18,7 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; @@ -25,6 +26,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Callable; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + import org.opendaylight.yangtools.binding.data.codec.impl.NodeCodecContext.CodecContextFactory; import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.concepts.Immutable; @@ -52,10 +56,13 @@ import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; class BindingCodecContext implements CodecContextFactory, Immutable { - + private static final Logger LOG = LoggerFactory.getLogger(BindingCodecContext.class); private static final String GETTER_PREFIX = "get"; + private final SchemaRootCodecContext root; private final BindingRuntimeContext context; private final Codec> instanceIdentifierCodec = @@ -72,7 +79,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable { return context; } - public Codec> getInstanceIdentifierCodec() { + Codec> getInstanceIdentifierCodec() { return instanceIdentifierCodec; } @@ -102,14 +109,27 @@ class BindingCodecContext implements CodecContextFactory, Immutable { return currentNode; } - public NodeCodecContext getCodecContextNode(final YangInstanceIdentifier dom, - final List builder) { + /** + * Multi-purpose utility function. Traverse the codec tree, looking for + * the appropriate codec for the specified {@link YangInstanceIdentifier}. + * As a side-effect, gather all traversed binding {@link InstanceIdentifier.PathArgument}s + * into the supplied collection. + * + * @param dom {@link YangInstanceIdentifier} which is to be translated + * @param bindingArguments Collection for traversed path arguments + * @return Codec for target node, or @null if the node does not have a + * binding representation (choice, case, leaf). + */ + @Nullable NodeCodecContext getCodecContextNode(final @Nonnull YangInstanceIdentifier dom, + final @Nonnull Collection bindingArguments) { NodeCodecContext currentNode = root; ListNodeCodecContext currentList = null; + for (YangInstanceIdentifier.PathArgument domArg : dom.getPathArguments()) { - Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext); - DataContainerCodecContext previous = (DataContainerCodecContext) currentNode; - NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg); + Preconditions.checkArgument(currentNode instanceof DataContainerCodecContext, "Unexpected child of non-container node %s", currentNode); + final DataContainerCodecContext previous = (DataContainerCodecContext) currentNode; + final NodeCodecContext nextNode = previous.getYangIdentifierChild(domArg); + /* * List representation in YANG Instance Identifier consists of two * arguments: first is list as a whole, second is list as an item so @@ -119,19 +139,13 @@ class BindingCodecContext implements CodecContextFactory, Immutable { * Identifier as Item or IdentifiableItem */ if (currentList != null) { + Preconditions.checkArgument(currentList == nextNode, "List should be referenced two times in YANG Instance Identifier %s", dom); - if (currentList == nextNode) { - - // We entered list, so now we have all information to emit - // list - // path using second list argument. - builder.add(currentList.getBindingPathArgument(domArg)); - currentList = null; - currentNode = nextNode; - } else { - throw new IllegalArgumentException( - "List should be referenced two times in YANG Instance Identifier"); - } + // We entered list, so now we have all information to emit + // list path using second list argument. + bindingArguments.add(currentList.getBindingPathArgument(domArg)); + currentList = null; + currentNode = nextNode; } else if (nextNode instanceof ListNodeCodecContext) { // We enter list, we do not update current Node yet, // since we need to verify @@ -141,24 +155,27 @@ class BindingCodecContext implements CodecContextFactory, Immutable { // it is not supported by binding instance identifier. currentNode = nextNode; } else if (nextNode instanceof DataContainerCodecContext) { - builder.add(((DataContainerCodecContext) nextNode).getBindingPathArgument(domArg)); + bindingArguments.add(((DataContainerCodecContext) nextNode).getBindingPathArgument(domArg)); currentNode = nextNode; } else if (nextNode instanceof LeafNodeCodecContext) { - Preconditions.checkArgument(builder == null, "Instance Identifier for leaf is not representable."); - + LOG.debug("Instance identifier referencing a leaf is not representable (%s)", dom); + return null; } } + // Algorithm ended in list as whole representation // we sill need to emit identifier for list - - if (builder != null) { - Preconditions.checkArgument(!(currentNode instanceof ChoiceNodeCodecContext), - "Instance Identifier for choice is not representable."); - Preconditions.checkArgument(!(currentNode instanceof CaseNodeCodecContext), - "Instance Identifier for case is not representable."); + if (currentNode instanceof ChoiceNodeCodecContext) { + LOG.debug("Instance identifier targeting a choice is not representable (%s)", dom); + return null; + } + if (currentNode instanceof CaseNodeCodecContext) { + LOG.debug("Instance identifier targeting a case is not representable (%s)", dom); + return null; } + if (currentList != null) { - builder.add(currentList.getBindingPathArgument(null)); + bindingArguments.add(currentList.getBindingPathArgument(null)); return currentList; } return currentNode; @@ -264,12 +281,11 @@ class BindingCodecContext implements CodecContextFactory, Immutable { } else if (rootType instanceof InstanceIdentifierTypeDefinition) { return ValueTypeCodec.encapsulatedValueCodecFor(valueType, instanceIdentifierCodec); } else if (rootType instanceof UnionTypeDefinition) { - Callable loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) rootType, - getInstanceIdentifierCodec(), getIdentityCodec()); + Callable loader = UnionTypeCodec.loader(valueType, (UnionTypeDefinition) rootType); try { return loader.call(); } catch (Exception e) { - throw new IllegalStateException("Unable to load codec for "+valueType,e); + throw new IllegalStateException("Unable to load codec for " + valueType, e); } } return ValueTypeCodec.getCodecFor(valueType, instantiatedType); @@ -279,16 +295,16 @@ class BindingCodecContext implements CodecContextFactory, Immutable { @Override public YangInstanceIdentifier serialize(final InstanceIdentifier input) { - List domArgs = new LinkedList<>(); + List domArgs = new ArrayList<>(); getCodecContextNode(input, domArgs); return YangInstanceIdentifier.create(domArgs); } @Override public InstanceIdentifier deserialize(final YangInstanceIdentifier input) { - List builder = new LinkedList<>(); - getCodecContextNode(input, builder); - return InstanceIdentifier.create(builder); + final List builder = new ArrayList<>(); + final NodeCodecContext codec = getCodecContextNode(input, builder); + return codec == null ? null : InstanceIdentifier.create(builder); } } @@ -341,7 +357,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable { } - private class IdentifiableItemCodec implements Codec> { + private static class IdentifiableItemCodec implements Codec> { private final ImmutableSortedMap keyValueContexts; private final ListSchemaNode schema; diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java index 9b772bd9dc..bfdad3efeb 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingNormalizedNodeCodecRegistry.java @@ -16,7 +16,7 @@ import com.google.common.cache.LoadingCache; import java.io.IOException; import java.util.AbstractMap.SimpleEntry; -import java.util.LinkedList; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -34,7 +34,9 @@ import org.opendaylight.yangtools.yang.binding.DataObjectSerializerRegistry; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; 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.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -74,23 +76,21 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR @Override public YangInstanceIdentifier toYangInstanceIdentifier(final InstanceIdentifier binding) { - List builder = new LinkedList<>(); - codecContext.getCodecContextNode(binding, builder); return codecContext.getInstanceIdentifierCodec().serialize(binding); } @Override public InstanceIdentifier fromYangInstanceIdentifier(final YangInstanceIdentifier dom) { return codecContext.getInstanceIdentifierCodec().deserialize(dom); - } + } @Override public Entry> toNormalizedNode(final InstanceIdentifier path, final T data) { NormalizedNodeResult result = new NormalizedNodeResult(); - // We create dom stream writer which produces normalized nodes + // We create DOM stream writer which produces normalized nodes NormalizedNodeStreamWriter domWriter = ImmutableNormalizedNodeStreamWriter.from(result); - // We create Binding Stream Writer wchich translates from Binding to Normalized Nodes + // We create Binding Stream Writer which translates from Binding to Normalized Nodes Entry writeCtx = codecContext.newWriter(path, domWriter); // We get serializer which reads binding data and uses Binding To Normalized Node writer to write result @@ -103,26 +103,51 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR return new SimpleEntry>(writeCtx.getKey(),result.getResult()); } - private boolean isBindingRepresentable(final NormalizedNode data) { - return !(data instanceof MapNode) && !(data instanceof UnkeyedListNode) && !(data instanceof LeafSetNode) && !(data instanceof LeafNode); - } + private static boolean isBindingRepresentable(final NormalizedNode data) { + if (data instanceof ChoiceNode) { + return false; + } + if (data instanceof LeafNode) { + return false; + } + if (data instanceof LeafSetNode) { + return false; + } + if( data instanceof LeafSetEntryNode) { + return false; + } + if (data instanceof MapNode) { + return false; + } + if (data instanceof UnkeyedListNode) { + return false; + } + return true; + } @Override - public Entry, DataObject> fromNormalizedNode(final YangInstanceIdentifier path, - final NormalizedNode data) { - List builder = new LinkedList<>(); - if(isBindingRepresentable(data)) { - DataObject lazyObj = (DataObject) codecContext.getCodecContextNode(path, builder).dataFromNormalizedNode(data); - InstanceIdentifier bindingPath = InstanceIdentifier.create(builder); - return new SimpleEntry, DataObject>(bindingPath,lazyObj); + public Entry, DataObject> fromNormalizedNode(final YangInstanceIdentifier path, final NormalizedNode data) { + if (!isBindingRepresentable(data)) { + return null; } - return null; + + final List builder = new ArrayList<>(); + final NodeCodecContext codec = codecContext.getCodecContextNode(path, builder); + if (codec == null) { + if (data != null) { + LOG.warn("Path {} does not have a binding equivalent, should have been caught earlier ({})", path, data.getClass()); + } + return null; + } + + final DataObject lazyObj = (DataObject) codec.dataFromNormalizedNode(data); + final InstanceIdentifier bindingPath = InstanceIdentifier.create(builder); + return new SimpleEntry, DataObject>(bindingPath, lazyObj); } @Override - public Map, DataObject> fromNormalizedNodes( - final Map> dom) { + public Map, DataObject> fromNormalizedNodes(final Map> dom) { throw new UnsupportedOperationException("Not implemented yet"); } @@ -144,8 +169,8 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR private static class DeserializeFunction implements Function>, Optional> { - private final DataObjectCodecContext ctx; + public DeserializeFunction(final DataObjectCodecContext ctx) { super(); this.ctx = ctx; @@ -159,8 +184,6 @@ public class BindingNormalizedNodeCodecRegistry implements DataObjectSerializerR } return Optional.absent(); } - - } private class GeneratorLoader extends CacheLoader, DataObjectSerializer> { diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java index 700476f86b..bac768703c 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java @@ -11,8 +11,13 @@ import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; + import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; @@ -36,20 +41,50 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext Map> byYangCaseChildBuilder = new HashMap<>(); Map, DataContainerCodecPrototype> byClassBuilder = new HashMap<>(); Map, DataContainerCodecPrototype> byCaseChildClassBuilder = new HashMap<>(); - + Set> potentialSubstitutions = new HashSet<>(); + // Walks all cases for supplied choice in current runtime context for (Class caze : factory().getRuntimeContext().getCases(bindingClass())) { + // We try to load case using exact match thus name + // and original schema must equals DataContainerCodecPrototype cazeDef = loadCase(caze); + // If we have case definition, this case is instantiated + // at current location and thus is valid in context of parent choice if (cazeDef != null) { byClassBuilder.put(cazeDef.getBindingClass(), cazeDef); + // Updates collection of case children for (Class cazeChild : BindingReflections.getChildrenClasses((Class) caze)) { byCaseChildClassBuilder.put(cazeChild, cazeDef); } + // Updates collection of YANG instance identifier to case for (DataSchemaNode cazeChild : cazeDef.getSchema().getChildNodes()) { byYangCaseChildBuilder.put(new NodeIdentifier(cazeChild.getQName()), cazeDef); } + } else { + /* + * If case definition is not available, we store it for + * later check if it could be used as substitution of existing one. + */ + potentialSubstitutions.add(caze); } } + Map, DataContainerCodecPrototype> bySubstitutionBuilder = new HashMap<>(); + /* + * Walks all cases which are not directly instantiated and + * tries to match them to instantiated cases - represent same data as instantiated case, + * only case name or schema path is different. This is required due property of + * binding specification, that if choice is in grouping schema path location is lost, + * and users may use incorrect case class using copy builders. + */ + for(Class substitution : potentialSubstitutions) { + search: for(Entry, DataContainerCodecPrototype> real : byClassBuilder.entrySet()) { + if(BindingReflections.isSubstitutionFor(substitution, real.getKey())) { + bySubstitutionBuilder.put(substitution, real.getValue()); + break search; + } + } + } + byClassBuilder.putAll(bySubstitutionBuilder); byYangCaseChild = ImmutableMap.copyOf(byYangCaseChildBuilder); byClass = ImmutableMap.copyOf(byClassBuilder); byCaseChildClass = ImmutableMap.copyOf(byCaseChildClassBuilder); diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java index 32fdf6d80e..5725e186ba 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java @@ -10,12 +10,16 @@ package org.opendaylight.yangtools.binding.data.codec.impl; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSortedMap; import java.lang.reflect.Method; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.SortedMap; +import java.util.TreeMap; import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.binding.Augmentable; @@ -41,9 +45,17 @@ import org.slf4j.LoggerFactory; abstract class DataObjectCodecContext extends DataContainerCodecContext { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); + private static final Comparator METHOD_BY_ALPHABET = new Comparator() { + + @Override + public int compare(final Method o1, final Method o2) { + return o1.getName().compareTo(o2.getName()); + } + }; + private final ImmutableMap leafChild; private final ImmutableMap byYang; - private final ImmutableMap byMethod; + private final ImmutableSortedMap byMethod; private final ImmutableMap, DataContainerCodecPrototype> byStreamClass; private final ImmutableMap, DataContainerCodecPrototype> byBindingArgClass; protected final Method augmentationGetter; @@ -56,7 +68,7 @@ abstract class DataObjectCodecContext extends DataC Map, Method> clsToMethod = BindingReflections.getChildrenClassToMethod(bindingClass()); Map byYangBuilder = new HashMap<>(); - Map byMethodBuilder = new HashMap<>(); + SortedMap byMethodBuilder = new TreeMap<>(METHOD_BY_ALPHABET); Map, DataContainerCodecPrototype> byStreamClassBuilder = new HashMap<>(); Map, DataContainerCodecPrototype> byBindingArgClassBuilder = new HashMap<>(); @@ -78,7 +90,7 @@ abstract class DataObjectCodecContext extends DataC } } } - this.byMethod = ImmutableMap.copyOf(byMethodBuilder); + this.byMethod = ImmutableSortedMap.copyOfSorted(byMethodBuilder); if (Augmentable.class.isAssignableFrom(bindingClass())) { try { augmentationGetter = bindingClass().getMethod("getAugmentation", Class.class); @@ -107,6 +119,29 @@ abstract class DataObjectCodecContext extends DataC @Override protected DataContainerCodecContext getStreamChild(final Class childClass) { DataContainerCodecPrototype childProto = byStreamClass.get(childClass); + if (childProto != null) { + return childProto.get(); + } + + if (Augmentation.class.isAssignableFrom(childClass)) { + /* + * It is potentially mismatched valid augmentation - we look up equivalent augmentation + * using reflection and walk all stream child and compare augmenations classes + * if they are equivalent. + * + * FIXME: Cache mapping of mismatched augmentation to real one, to speed up lookup. + */ + Class augTarget = BindingReflections.findAugmentationTarget((Class) childClass); + if ((bindingClass().equals(augTarget))) { + for (DataContainerCodecPrototype realChild : byStreamClass.values()) { + if (Augmentation.class.isAssignableFrom(realChild.getBindingClass()) + && BindingReflections.isSubstitutionFor(childClass,realChild.getBindingClass())) { + childProto = realChild; + break; + } + } + } + } Preconditions.checkArgument(childProto != null, " Child %s is not valid child.",childClass); return childProto.get(); } @@ -243,4 +278,4 @@ abstract class DataObjectCodecContext extends DataC return byMethod.keySet(); } -} \ No newline at end of file +} diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java index 6cbcc5c156..f94673c288 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java @@ -8,13 +8,14 @@ package org.opendaylight.yangtools.binding.data.codec.impl; import com.google.common.collect.ImmutableSet; + import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; -import org.opendaylight.yangtools.concepts.Codec; + import org.opendaylight.yangtools.yang.binding.BindingMapping; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; @@ -34,12 +35,10 @@ final class UnionTypeCodec extends ReflectionBasedCodec { } } - @SuppressWarnings("rawtypes") - static final Callable loader(final Class unionCls,final UnionTypeDefinition unionType, final Codec instanceIdentifier, final Codec identity) { + static final Callable loader(final Class unionCls, final UnionTypeDefinition unionType) { return new Callable() { - @Override - public UnionTypeCodec call() throws Exception { + public UnionTypeCodec call() throws NoSuchMethodException, SecurityException { Set values = new HashSet<>(); for(TypeDefinition subtype : unionType.getTypes()) { String methodName = "get" + BindingMapping.getClassName(subtype.getQName()); diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java new file mode 100644 index 0000000000..a56364cad1 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AbstractBindingRuntimeTest.java @@ -0,0 +1,37 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import org.junit.Before; +import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleInfoBackedContext; +import org.opendaylight.yangtools.sal.binding.generator.util.BindingRuntimeContext; +import org.opendaylight.yangtools.yang.binding.util.BindingReflections; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +public abstract class AbstractBindingRuntimeTest { + + private SchemaContext schemaContext; + private BindingRuntimeContext runtimeContext; + + @Before + public void setup() { + ModuleInfoBackedContext ctx = ModuleInfoBackedContext.create(); + ctx.addModuleInfos(BindingReflections.loadModuleInfos()); + schemaContext = ctx.tryToCreateSchemaContext().get(); + runtimeContext = BindingRuntimeContext.create(ctx, schemaContext); + + } + + public SchemaContext getSchemaContext() { + return schemaContext; + } + + public BindingRuntimeContext getRuntimeContext() { + return runtimeContext; + } +} diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java new file mode 100644 index 0000000000..62d913f8b6 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/AugmentationSubstitutionTest.java @@ -0,0 +1,80 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import javassist.ClassPool; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + + +public class AugmentationSubstitutionTest extends AbstractBindingRuntimeTest { + + private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class, TOP_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST + .augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST + .augmentation(TreeComplexUsesAugment.class); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + private BindingNormalizedNodeCodecRegistry registry; + + @Override + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void augmentationInGroupingSubstituted() { + TopLevelList baRpc = new TopLevelListBuilder() + .setKey(TOP_FOO_KEY) + .addAugmentation(RpcComplexUsesAugment.class, new RpcComplexUsesAugmentBuilder(createComplexData()).build()) + .build(); + TopLevelList baTree = new TopLevelListBuilder() + .setKey(TOP_FOO_KEY) + .addAugmentation(TreeComplexUsesAugment.class, new TreeComplexUsesAugmentBuilder(createComplexData()).build()) + .build(); + NormalizedNode domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue(); + NormalizedNode domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue(); + assertEquals(domTreeEntry, domRpcEntry); + } + + private RpcComplexUsesAugment createComplexData() { + return new RpcComplexUsesAugmentBuilder() + .setContainerWithUses(new ContainerWithUsesBuilder() + .setLeafFromGrouping("foo") + .build()) + .setListViaUses(Collections.emptyList()) + .build(); + } + +} diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java new file mode 100644 index 0000000000..a01e12d172 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/CaseSubstitutionTest.java @@ -0,0 +1,81 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import javassist.ClassPool; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.put.top.input.top.level.list.choice.in.list.ComplexViaUsesWithDifferentNameBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.top.top.level.list.choice.in.list.ComplexViaUsesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; + + +public class CaseSubstitutionTest extends AbstractBindingRuntimeTest { + + private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class, TOP_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST + .augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST + .augmentation(TreeComplexUsesAugment.class); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + private BindingNormalizedNodeCodecRegistry registry; + + @Override + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void choiceInGroupingSubstituted() { + TopLevelList baRpc = new TopLevelListBuilder() + .setKey(TOP_FOO_KEY) + .setChoiceInList(new ComplexViaUsesWithDifferentNameBuilder(createComplexData()).build()) + .build(); + TopLevelList baTree = new TopLevelListBuilder() + .setKey(TOP_FOO_KEY) + .setChoiceInList(new ComplexViaUsesBuilder(createComplexData()).build()) + .build(); + NormalizedNode domTreeEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baTree).getValue(); + NormalizedNode domRpcEntry = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, baRpc).getValue(); + assertEquals(domTreeEntry, domRpcEntry); + } + + private RpcComplexUsesAugment createComplexData() { + return new RpcComplexUsesAugmentBuilder() + .setContainerWithUses(new ContainerWithUsesBuilder() + .setLeafFromGrouping("foo") + .build()) + .setListViaUses(Collections.emptyList()) + .build(); + } + +} diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java new file mode 100644 index 0000000000..67b7d1f903 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierSerializeDeserializeTest.java @@ -0,0 +1,128 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import com.google.common.collect.Iterables; +import javassist.ClassPool; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.Identifier; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class InstanceIdentifierSerializeDeserializeTest extends AbstractBindingRuntimeTest{ + public static final String TOP_LEVEL_LIST_KEY_VALUE = "foo"; + + private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier + .builder(Top.class).child(TopLevelList.class, TOP_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = + BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = + BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class); + + public static final QName TOP_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "top"); + public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list"); + public static final QName TOP_LEVEL_LIST_KEY = QName.create(TOP_QNAME, "name"); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.builder().node(TOP_QNAME).build(); + public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME); + public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_1_PATH = BI_TOP_LEVEL_LIST_PATH + .node(new YangInstanceIdentifier.NodeIdentifierWithPredicates(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY, TOP_LEVEL_LIST_KEY_VALUE)); + + private BindingNormalizedNodeCodecRegistry registry; + + @Before + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void testYangIIToBindingAwareII() { + InstanceIdentifier instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_PATH); + assertEquals(Top.class, instanceIdentifier.getTargetType()); + } + + @Test + public void testYangIIToBindingAwareIIListWildcarded() { + InstanceIdentifier instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_LEVEL_LIST_PATH); + assertEquals(TopLevelList.class, instanceIdentifier.getTargetType()); + assertTrue(instanceIdentifier.isWildcarded()); + } + + @Test + public void testYangIIToBindingAwareIIListWithKey() { + InstanceIdentifier instanceIdentifier = registry.fromYangInstanceIdentifier(BI_TOP_LEVEL_LIST_1_PATH); + InstanceIdentifier.PathArgument last = Iterables.getLast(instanceIdentifier.getPathArguments()); + assertEquals(TopLevelList.class, instanceIdentifier.getTargetType()); + assertFalse(instanceIdentifier.isWildcarded()); + assertTrue(last instanceof InstanceIdentifier.IdentifiableItem); + Identifier key = ((InstanceIdentifier.IdentifiableItem) last).getKey(); + assertEquals(TopLevelListKey.class, key.getClass()); + assertEquals(TOP_LEVEL_LIST_KEY_VALUE, ((TopLevelListKey)key).getName()); + } + + @Test + public void testBindingAwareIIToYangIContainer() { + YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier( + InstanceIdentifier.create(Top.class).child(TopLevelList.class)); + YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument(); + assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifier); + assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType()); + } + + @Test + public void testBindingAwareIIToYangIIWildcard() { + YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier( + InstanceIdentifier.create(Top.class).child(TopLevelList.class)); + YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument(); + assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifier); + assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType()); + } + + @Test + public void testBindingAwareIIToYangIIListWithKey() { + YangInstanceIdentifier yangInstanceIdentifier = registry.toYangInstanceIdentifier( + InstanceIdentifier.create(Top.class).child(TopLevelList.class, TOP_FOO_KEY)); + YangInstanceIdentifier.PathArgument lastPathArgument = yangInstanceIdentifier.getLastPathArgument(); + assertTrue(lastPathArgument instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates); + assertTrue(((YangInstanceIdentifier.NodeIdentifierWithPredicates) lastPathArgument).getKeyValues().containsValue(TOP_LEVEL_LIST_KEY_VALUE)); + assertEquals(TopLevelList.QNAME, lastPathArgument.getNodeType()); + } + + @Test + public void testBindingAwareIIToYangIIAugmentation() { + YangInstanceIdentifier.PathArgument lastArg = registry.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES).getLastPathArgument(); + assertTrue(lastArg instanceof YangInstanceIdentifier.AugmentationIdentifier); + } + + @Test + public void testBindingAwareIIToYangIILeafOnlyAugmentation() { + YangInstanceIdentifier.PathArgument leafOnlyLastArg = registry.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument(); + assertTrue(leafOnlyLastArg instanceof YangInstanceIdentifier.AugmentationIdentifier); + assertTrue(((YangInstanceIdentifier.AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME)); + } +} diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java new file mode 100644 index 0000000000..e011017894 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/InstanceIdentifierTest.java @@ -0,0 +1,67 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import javassist.ClassPool; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + + +public class InstanceIdentifierTest extends AbstractBindingRuntimeTest { + + private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class, TOP_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST + .augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST + .augmentation(TreeComplexUsesAugment.class); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + private BindingNormalizedNodeCodecRegistry registry; + + @Override + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void testComplexAugmentationSerialization() { + YangInstanceIdentifier yangII = registry.toYangInstanceIdentifier(BA_TREE_COMPLEX_USES); + PathArgument lastArg = yangII.getLastPathArgument(); + assertTrue("Last argument should be AugmentationIdentifier", lastArg instanceof AugmentationIdentifier); + InstanceIdentifier bindingII = registry.fromYangInstanceIdentifier(yangII); + assertEquals(BA_TREE_COMPLEX_USES, bindingII); + } + + @Test + public void testLeafOnlyAugmentationSerialization() { + PathArgument leafOnlyLastArg = registry.toYangInstanceIdentifier(BA_TREE_LEAF_ONLY).getLastPathArgument(); + assertTrue("Last argument should be AugmentationIdentifier", leafOnlyLastArg instanceof AugmentationIdentifier); + assertTrue(((AugmentationIdentifier) leafOnlyLastArg).getPossibleChildNames().contains(SIMPLE_VALUE_QNAME)); + } + +} diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java new file mode 100644 index 0000000000..4b08fbe17f --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/NormalizedNodeSerializeDeserializeTest.java @@ -0,0 +1,277 @@ +/* + * 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.yangtools.binding.data.codec.test; + +import javassist.ClassPool; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.ChoiceContainer; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.ChoiceContainerBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.TopBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.choice.identifier.ExtendedBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.choice.identifier.extended.ExtendedIdBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListKey; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableAugmentationNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableLeafSetEntryNodeBuilder; +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.ImmutableOrderedMapNodeBuilder; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.top; +import static org.opendaylight.controller.md.sal.test.model.util.ListsBindingUtils.topLevelList; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntry; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder; +import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder; + +public class NormalizedNodeSerializeDeserializeTest extends AbstractBindingRuntimeTest{ + + public static final String TOP_LEVEL_LIST_FOO_KEY_VALUE = "foo"; + public static final TopLevelListKey TOP_LEVEL_LIST_FOO_KEY = new TopLevelListKey(TOP_LEVEL_LIST_FOO_KEY_VALUE); + + public static final QName TOP_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "top"); + public static final QName TOP_LEVEL_LIST_QNAME = QName.create(TOP_QNAME, "top-level-list"); + public static final QName TOP_LEVEL_LIST_KEY_QNAME = QName.create(TOP_QNAME, "name"); + public static final QName TOP_LEVEL_LEAF_LIST_QNAME = QName.create(TOP_QNAME, "top-level-leaf-list"); + public static final QName NESTED_LIST_QNAME = QName.create(TOP_QNAME, "nested-list"); + public static final QName NESTED_LIST_KEY_QNAME = QName.create(TOP_QNAME, "name"); + public static final QName CHOICE_CONTAINER_QNAME = + QName.create("urn:opendaylight:params:xml:ns:yang:yangtools:test:binding", "2014-07-01", "choice-container"); + public static final QName CHOICE_IDENTIFIER_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "identifier"); + public static final QName CHOICE_IDENTIFIER_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "id"); + public static final QName SIMPLE_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "simple-id"); + public static final QName EXTENDED_ID_QNAME = QName.create(CHOICE_CONTAINER_QNAME, "extended-id"); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier + .builder(Top.class).child(TopLevelList.class, TOP_LEVEL_LIST_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = + BA_TOP_LEVEL_LIST.augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = + BA_TOP_LEVEL_LIST.augmentation(TreeComplexUsesAugment.class); + + public static final YangInstanceIdentifier BI_TOP_PATH = YangInstanceIdentifier.of(TOP_QNAME); + public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_PATH = BI_TOP_PATH.node(TOP_LEVEL_LIST_QNAME); + public static final YangInstanceIdentifier BI_TOP_LEVEL_LIST_FOO_PATH = BI_TOP_LEVEL_LIST_PATH + .node(new YangInstanceIdentifier.NodeIdentifierWithPredicates(TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)); + public static final YangInstanceIdentifier BI_CHOICE_CONTAINER_PATH = YangInstanceIdentifier.of(CHOICE_CONTAINER_QNAME); + + private BindingNormalizedNodeCodecRegistry registry; + + @Before + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void containerToNormalized() { + Map.Entry> entry = + registry.toNormalizedNode(InstanceIdentifier.create(Top.class), top()); + ContainerNode topNormalized = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME)) + .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build(); + assertEquals(topNormalized, entry.getValue()); + } + + @Test + public void containerFromNormalized() { + ContainerNode topNormalized = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME)) + .withChild(mapNodeBuilder(TOP_LEVEL_LIST_QNAME).build()).build(); + Map.Entry, DataObject> entry = registry.fromNormalizedNode(BI_TOP_PATH, topNormalized); + assertEquals(top(), entry.getValue()); + } + + @Test + public void listWithKeysToNormalized() { + Map.Entry> entry = + registry.toNormalizedNode(BA_TOP_LEVEL_LIST, topLevelList(TOP_LEVEL_LIST_FOO_KEY)); + MapEntryNode topLevelListNormalized = ImmutableMapEntryNodeBuilder.create() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates( + TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .build(); + assertEquals(topLevelListNormalized, entry.getValue()); + } + + @Test + public void listWithKeysFromNormalized() { + MapEntryNode topLevelListNormalized = ImmutableMapEntryNodeBuilder.create() + .withNodeIdentifier( + new YangInstanceIdentifier.NodeIdentifierWithPredicates( + TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .build(); + Map.Entry, DataObject> entry = + registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, topLevelListNormalized); + assertEquals(topLevelList(TOP_LEVEL_LIST_FOO_KEY), entry.getValue()); + } + + @Test + public void leafOnlyAugmentationToNormalized() { + Map.Entry> entry = + registry.toNormalizedNode(BA_TREE_LEAF_ONLY, new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build()); + Set augmentationChildren = new HashSet<>(); + augmentationChildren.add(SIMPLE_VALUE_QNAME); + AugmentationNode augmentationNode = ImmutableAugmentationNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren)) + .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) + .build(); + assertEquals(augmentationNode, entry.getValue()); + } + + @Test + public void leafOnlyAugmentationFromNormalized() { + Set augmentationChildren = new HashSet<>(); + augmentationChildren.add(SIMPLE_VALUE_QNAME); + AugmentationNode augmentationNode = ImmutableAugmentationNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren)) + .withChild(leafNode(SIMPLE_VALUE_QNAME, "simpleValue")) + .build(); + Map.Entry, DataObject> entry = registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH.node( + new YangInstanceIdentifier.AugmentationIdentifier(augmentationChildren)), augmentationNode); + assertEquals(new TreeLeafOnlyAugmentBuilder().setSimpleValue("simpleValue").build(), entry.getValue()); + } + + @Test + public void leafListToNormalized() { + List topLevelLeafList = new ArrayList<>(); + topLevelLeafList.add("foo"); + Top top = new TopBuilder().setTopLevelLeafList(topLevelLeafList).build(); + + Map.Entry> entry = + registry.toNormalizedNode(InstanceIdentifier.create(Top.class), top); + ContainerNode containerNode = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME)) + .withChild(ImmutableLeafSetNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME)) + .withChild( + ImmutableLeafSetEntryNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeWithValue(TOP_LEVEL_LEAF_LIST_QNAME, "foo")) + .withValue("foo") + .build()) + .build()) + .build(); + assertEquals(containerNode, entry.getValue()); + } + + @Test + public void leafListFromNormalized() { + ContainerNode topWithLeafList = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_QNAME)) + .withChild(ImmutableLeafSetNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TOP_LEVEL_LEAF_LIST_QNAME)) + .withChild(ImmutableLeafSetEntryNodeBuilder.create().withNodeIdentifier( + new YangInstanceIdentifier.NodeWithValue(TOP_LEVEL_LEAF_LIST_QNAME, "foo")).withValue("foo").build()).build()) + .build(); + Map.Entry, DataObject> entry = registry.fromNormalizedNode(BI_TOP_PATH, topWithLeafList); + List topLevelLeafList = new ArrayList<>(); + topLevelLeafList.add("foo"); + Top top = new TopBuilder().setTopLevelLeafList(topLevelLeafList).build(); + assertEquals(top, entry.getValue()); + } + + @Test + public void choiceToNormalized() { + ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder().setExtendedId( + new ExtendedIdBuilder().setId("identifier_value").build()).build()).build(); + Map.Entry> entry = + registry.toNormalizedNode(InstanceIdentifier.create(ChoiceContainer.class), choiceContainerBA); + ContainerNode choiceContainer = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_CONTAINER_QNAME)) + .withChild(ImmutableChoiceNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_IDENTIFIER_QNAME)) + .withChild(ImmutableContainerNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(EXTENDED_ID_QNAME)) + .withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value")).build()).build()) + .build(); + assertEquals(choiceContainer, entry.getValue()); + } + + @Test + public void choiceFromNormalized() { + ContainerNode choiceContainerBI = ImmutableContainerNodeBuilder.create() + .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_CONTAINER_QNAME)) + .withChild(ImmutableChoiceNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CHOICE_IDENTIFIER_QNAME)) + .withChild(ImmutableContainerNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(EXTENDED_ID_QNAME)) + .withChild(leafNode(CHOICE_IDENTIFIER_ID_QNAME, "identifier_value")).build()).build()) + .build(); + Map.Entry, DataObject> entry = registry.fromNormalizedNode(BI_CHOICE_CONTAINER_PATH, choiceContainerBI); + ChoiceContainer choiceContainerBA = new ChoiceContainerBuilder().setIdentifier(new ExtendedBuilder().setExtendedId( + new ExtendedIdBuilder().setId("identifier_value").build()).build()).build(); + assertEquals(choiceContainerBA, entry.getValue()); + } + + @Test + public void orderedLisToNormalized() { + InstanceIdentifier ii = BA_TOP_LEVEL_LIST; + List nestedLists = new ArrayList<>(); + nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("foo")).build()); + nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("bar")).build()); + TopLevelList topLevelList = new TopLevelListBuilder().setKey(TOP_LEVEL_LIST_FOO_KEY).setNestedList(nestedLists).build(); + Map.Entry> entry = registry.toNormalizedNode(ii, topLevelList); + MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates( + TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild( + ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) + .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo")) + .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build(); + assertEquals(foo, entry.getValue()); + } + + @Test + public void orderedLisFromNormalized() { + MapEntryNode foo = mapEntryBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifierWithPredicates( + TOP_LEVEL_LIST_QNAME, TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild(leafNode(TOP_LEVEL_LIST_KEY_QNAME, TOP_LEVEL_LIST_FOO_KEY_VALUE)) + .withChild( + ImmutableOrderedMapNodeBuilder.create().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(NESTED_LIST_QNAME)) + .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "foo")) + .withChild(mapEntry(NESTED_LIST_QNAME, NESTED_LIST_KEY_QNAME, "bar")).build()).build(); + InstanceIdentifier ii = BA_TOP_LEVEL_LIST; + + Map.Entry, DataObject> entry = registry.fromNormalizedNode(BI_TOP_LEVEL_LIST_FOO_PATH, foo); + List nestedLists = new ArrayList<>(); + nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("foo")).build()); + nestedLists.add(new NestedListBuilder().setKey(new NestedListKey("bar")).build()); + TopLevelList topLevelList = new TopLevelListBuilder().setKey(TOP_LEVEL_LIST_FOO_KEY).setNestedList(nestedLists).build(); + assertEquals(topLevelList, entry.getValue()); + } +} diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java index befe3e88e0..b98c48c984 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java @@ -30,7 +30,6 @@ import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findP import com.google.common.base.Splitter; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -39,7 +38,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; - import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.binding.generator.util.BindingTypes; import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; @@ -310,7 +308,7 @@ public class BindingGeneratorImpl implements BindingGenerator { return null; } final String packageName = packageNameForGeneratedType(basePackageName, node.getPath()); - final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf); + final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, node, childOf, module); genType.addComment(node.getDescription()); genType.setDescription(createDescription(node, genType.getFullyQualifiedName())); genType.setModuleName(module.getName()); @@ -320,23 +318,61 @@ public class BindingGeneratorImpl implements BindingGenerator { genCtx.get(module).addChildNodeType(node, genType); groupingsToGenTypes(module, ((DataNodeContainer) node).getGroupings()); processUsesAugments((DataNodeContainer) node, module); + if (node.isAddedByUses() || node.isAugmenting()) + genType.setSuitableForBoxing(false); } return genType; } + private boolean hasWhenOrMustConstraints(final SchemaNode node) { + boolean hasWhenCondition; + boolean hasMustConstraints; + + if (node instanceof ContainerSchemaNode) { + ContainerSchemaNode contNode = (ContainerSchemaNode)node; + hasWhenCondition = contNode.getConstraints().getWhenCondition() != null; + hasMustConstraints = !isNullOrEmpty(contNode.getConstraints().getMustConstraints()); + + if (hasWhenCondition || hasMustConstraints) + return true; + } + return false; + } + private void containerToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ContainerSchemaNode node) { final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node); + if (genType != null) { constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), genType); resolveDataSchemaNodes(module, basePackageName, genType, genType, node.getChildNodes()); + + final String parentName = parent.getName(); + final String childOfName = childOf.getName(); + + if (parent != null && !parent.getName().contains("Data")) + genType.setParentType(parent); + genType.setSuitableForBoxing(hasOnlyOneChild(node) && !hasWhenOrMustConstraints(node)); + + if (parentName.equals(childOfName)) + genType.setSuitableForBoxing(false); } } + private boolean hasOnlyOneChild(final ContainerSchemaNode contNode) { + if (!isNullOrEmpty(contNode.getChildNodes()) && contNode.getChildNodes().size() == 1) + return true; + return false; + } + private void listToGenType(final Module module, final String basePackageName, final GeneratedTypeBuilder parent, final GeneratedTypeBuilder childOf, final ListSchemaNode node) { final GeneratedTypeBuilder genType = processDataSchemaNode(module, basePackageName, childOf, node); + if (genType != null) { + if (!parent.getName().equals(childOf) && !parent.getName().contains("Data")) { + genType.setParentType(parent); + } constructGetter(parent, node.getQName().getLocalName(), node.getDescription(), Types.listTypeFor(genType)); final List listKeys = listKeys(node); @@ -556,7 +592,7 @@ public class BindingGeneratorImpl implements BindingGenerator { processUsesAugments(notification, module); final GeneratedTypeBuilder notificationInterface = addDefaultInterfaceDefinition(basePackageName, - notification, BindingTypes.DATA_OBJECT); + notification, BindingTypes.DATA_OBJECT, module); notificationInterface.addImplementsType(NOTIFICATION); genCtx.get(module).addChildNodeType(notification, notificationInterface); @@ -704,7 +740,7 @@ public class BindingGeneratorImpl implements BindingGenerator { */ private void groupingToGenType(final String basePackageName, final GroupingDefinition grouping, final Module module) { final String packageName = packageNameForGeneratedType(basePackageName, grouping.getPath()); - final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping); + final GeneratedTypeBuilder genType = addDefaultInterfaceDefinition(packageName, grouping, module); genCtx.get(module).addGroupingType(grouping.getPath(), genType); resolveDataSchemaNodes(module, basePackageName, genType, genType, grouping.getChildNodes()); groupingsToGenTypes(module, grouping.getGroupings()); @@ -858,6 +894,7 @@ public class BindingGeneratorImpl implements BindingGenerator { if (targetTypeBuilder == null) { throw new NullPointerException("Target type not yet generated: " + targetSchemaNode); } + targetTypeBuilder.setSuitableForBoxing(false); if (!(targetSchemaNode instanceof ChoiceNode)) { String packageName = augmentPackageName; @@ -1158,6 +1195,7 @@ public class BindingGeneratorImpl implements BindingGenerator { constructGetter(parent, choiceNode.getQName().getLocalName(), choiceNode.getDescription(), choiceTypeBuilder); choiceTypeBuilder.addImplementsType(typeForClass(DataContainer.class)); + choiceTypeBuilder.setParentType(parent); genCtx.get(module).addChildNodeType(choiceNode, choiceTypeBuilder); generateTypesFromChoiceCases(module, basePackageName, choiceTypeBuilder.toInstance(), choiceNode); } @@ -1204,7 +1242,7 @@ public class BindingGeneratorImpl implements BindingGenerator { for (ChoiceCaseNode caseNode : caseNodes) { if (caseNode != null && !caseNode.isAddedByUses() && !caseNode.isAugmenting()) { final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath()); - final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode); + final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module); caseTypeBuilder.addImplementsType(refChoiceType); genCtx.get(module).addCaseType(caseNode.getPath(), caseTypeBuilder); genCtx.get(module).addChoiceToCaseMapping(refChoiceType, caseTypeBuilder, caseNode); @@ -1283,7 +1321,7 @@ public class BindingGeneratorImpl implements BindingGenerator { for (DataSchemaNode caseNode : augmentedNodes) { if (caseNode != null) { final String packageName = packageNameForGeneratedType(basePackageName, caseNode.getPath()); - final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode); + final GeneratedTypeBuilder caseTypeBuilder = addDefaultInterfaceDefinition(packageName, caseNode, module); caseTypeBuilder.addImplementsType(targetType); SchemaNode parent = null; @@ -1600,8 +1638,9 @@ public class BindingGeneratorImpl implements BindingGenerator { return returnType.toInstance(); } - private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode) { - return addDefaultInterfaceDefinition(packageName, schemaNode, null); + private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode, + final Module module) { + return addDefaultInterfaceDefinition(packageName, schemaNode, null, module); } /** @@ -1628,7 +1667,7 @@ public class BindingGeneratorImpl implements BindingGenerator { * @return generated type builder schemaNode */ private GeneratedTypeBuilder addDefaultInterfaceDefinition(final String packageName, final SchemaNode schemaNode, - final Type parent) { + final Type parent, final Module module) { final GeneratedTypeBuilder it = addRawInterfaceDefinition(packageName, schemaNode, ""); if (parent == null) { it.addImplementsType(DATA_OBJECT); @@ -1640,6 +1679,7 @@ public class BindingGeneratorImpl implements BindingGenerator { } if (schemaNode instanceof DataNodeContainer) { + groupingsToGenTypes(module, ((DataNodeContainer) schemaNode).getGroupings()); addImplementedInterfaceFromUses((DataNodeContainer) schemaNode, it); } @@ -1918,11 +1958,12 @@ public class BindingGeneratorImpl implements BindingGenerator { genTOBuilders.addAll(types); GeneratedTOBuilder resultTOBuilder = null; - if (!types.isEmpty()) { - resultTOBuilder = types.remove(0); - for (GeneratedTOBuilder genTOBuilder : types) { - resultTOBuilder.addEnclosingTransferObject(genTOBuilder); - } + if (types.isEmpty()) { + throw new IllegalStateException("No GeneratedTOBuilder objects generated from union " + typeDef); + } + resultTOBuilder = types.remove(0); + for (GeneratedTOBuilder genTOBuilder : types) { + resultTOBuilder.addEnclosingTransferObject(genTOBuilder); } final GeneratedPropertyBuilder genPropBuilder = resultTOBuilder.addProperty("value"); diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java index 7c8586ed8f..e532be5b9e 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java @@ -8,7 +8,6 @@ package org.opendaylight.yangtools.sal.binding.generator.impl; import com.google.common.collect.ImmutableList; - import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -19,7 +18,6 @@ import java.util.Map; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; - import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.DataObject; @@ -29,10 +27,10 @@ import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.CompositeNode; +import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.Node; import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.SimpleNodeTOImpl; import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry; @@ -60,9 +58,9 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec { public InstanceIdentifier deserialize( final org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier input) { Class baType = null; - List biArgs = input.getPath(); - List scannedPath = new ArrayList<>(biArgs.size()); - List baArgs = new ArrayList(biArgs.size()); + Iterable biArgs = input.getPathArguments(); + List scannedPath = new ArrayList<>(); + List baArgs = new ArrayList(); for (org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument biArg : biArgs) { scannedPath.add(biArg.getNodeType()); diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java index 92047d1f22..f72856e1c1 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java @@ -15,7 +15,6 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; - import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -30,7 +29,6 @@ import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; - import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; import org.opendaylight.yangtools.binding.generator.util.Types; import org.opendaylight.yangtools.concepts.Delegator; @@ -268,7 +266,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener while (iterator.hasNext()) { QName arg = iterator.next(); DataSchemaNode currentNode = previous.getDataChildByName(arg); - if (currentNode == null && previous instanceof DataNodeContainer) { + if (currentNode == null) { currentNode = searchInChoices(previous, arg); } if (currentNode instanceof DataNodeContainer) { @@ -852,7 +850,7 @@ class LazyGeneratedCodecRegistry implements CodecRegistry, SchemaContextListener if (!parentQName.equals(choiceName)) { // This item is instantiation of choice via uses in other YANG // module - if (choiceName.getNamespace().equals(schema.getQName())) { + if (choiceName.getNamespace().equals(schema.getQName().getNamespace())) { // Original definition of grouping is in same namespace // as original definition of case // so for sure case is introduced via instantiation of diff --git a/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/EnumerationBuilderImpl.java b/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/EnumerationBuilderImpl.java index cb7f50549e..ae80a51da4 100644 --- a/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/EnumerationBuilderImpl.java +++ b/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/EnumerationBuilderImpl.java @@ -342,6 +342,11 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En return values; } + @Override + public boolean isSuitableForBoxing() { + return false; + } + @Override public List getAnnotations() { return annotations; @@ -518,5 +523,6 @@ public final class EnumerationBuilderImpl extends AbstractBaseType implements En public String getModuleName() { return moduleName; } + } } diff --git a/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/GeneratedTOBuilderImpl.java b/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/GeneratedTOBuilderImpl.java index 270b04ac81..8d1bcea0e8 100644 --- a/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/GeneratedTOBuilderImpl.java +++ b/code-generator/binding-generator-util/src/main/java/org/opendaylight/yangtools/binding/generator/util/generated/type/builder/GeneratedTOBuilderImpl.java @@ -337,5 +337,10 @@ public final class GeneratedTOBuilderImpl extends AbstractGeneratedTypeBuilder schemaPath; + private boolean isSuitableForBoxing; + private GeneratedTypeBuilder parentType; public GeneratedTypeBuilderImpl(final String packageName, final String name) { super(packageName, name); @@ -49,6 +52,21 @@ public final class GeneratedTypeBuilderImpl extends AbstractGeneratedTypeBuilder this.reference = reference; } + @Override + public void setSuitableForBoxing(boolean value) { + this.isSuitableForBoxing = value; + } + + @Override + public void setParentType(GeneratedTypeBuilder parent) { + this.parentType = parent; + } + + @Override + public Type getParent() { + return this.parentType; + } + @Override public String toString() { StringBuilder builder = new StringBuilder(); @@ -86,6 +104,8 @@ public final class GeneratedTypeBuilderImpl extends AbstractGeneratedTypeBuilder private final String reference; private final String moduleName; private final Iterable schemaPath; + private final boolean isSuitableForBoxing; + private final GeneratedTypeBuilder parentType; public GeneratedTypeImpl(GeneratedTypeBuilderImpl builder) { super(builder); @@ -94,6 +114,8 @@ public final class GeneratedTypeBuilderImpl extends AbstractGeneratedTypeBuilder this.reference = builder.reference; this.moduleName = builder.moduleName; this.schemaPath = builder.schemaPath; + this.isSuitableForBoxing = builder.isSuitableForBoxing; + this.parentType = builder.parentType; } @Override @@ -115,5 +137,11 @@ public final class GeneratedTypeBuilderImpl extends AbstractGeneratedTypeBuilder public String getModuleName() { return moduleName; } + + @Override + public boolean isSuitableForBoxing() { + return isSuitableForBoxing; + } } + } diff --git a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend index 286b5f06b1..66b72cf9e6 100644 --- a/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend +++ b/code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/BuilderTemplate.xtend @@ -29,6 +29,7 @@ import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature import org.opendaylight.yangtools.sal.binding.model.api.Type import org.opendaylight.yangtools.yang.binding.Augmentable +import org.opendaylight.yangtools.yang.binding.ChildOf import org.opendaylight.yangtools.yang.binding.DataObject import org.opendaylight.yangtools.yang.binding.Identifiable @@ -213,7 +214,7 @@ class BuilderTemplate extends BaseTemplate { «generateFields(false)» - «generateAugmentField(true)» + «generateAugmentField(false)» «generateConstructorsFromIfcs(type)» @@ -225,9 +226,9 @@ class BuilderTemplate extends BaseTemplate { «generateSetters» - public «type.name» build() { - return new «type.name»«IMPL»(this); - } + «generateBuildMethod» + + «generateBuildBoxedMethod» private static final class «type.name»«IMPL» implements «type.name» { @@ -235,7 +236,7 @@ class BuilderTemplate extends BaseTemplate { «generateFields(true)» - «generateAugmentField(false)» + «generateAugmentField(true)» «generateCopyConstructor(true)» @@ -251,6 +252,53 @@ class BuilderTemplate extends BaseTemplate { } ''' + def private generateBuildMethod() ''' + public «type.name» build() { + return new «type.name»«IMPL»(this); + } + ''' + + def private generateBuildBoxedMethod() { + if(type.suitableForBoxing && type.parentType != null && isContainerAndIsNotList(type)) { + val parentTypeBuilder = createParentTypeBuilder() + if (countMatches(parentTypeBuilder, "org") < 2) { + return ''' + public «type.parentType.importedName» buildBoxed() { + return new «parentTypeBuilder»().set«type.name»(build()).build(); + } + ''' + } + } + return '' + } + + def private int countMatches(String string, String subString) { + if (string.nullOrEmpty || subString.nullOrEmpty) { + return 0 + } + var int count = 0; + var int idx = 0; + while ((idx = string.indexOf(subString, idx)) != -1) { + count = count + 1; + idx = idx + subString.length(); + } + return count; + } + + def private createParentTypeBuilder() { + return type.parentType.packageName + "." + type.parentType.importedName + "Builder" + } + + def private boolean isContainerAndIsNotList(GeneratedType type) { + val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type)) + val implementsChildOf = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(ChildOf), type)) + + if (implementsChildOf && !isList) { + return true + } + return false; + } + /** * Generate default constructor and constructor for every implemented interface from uses statements. */ @@ -421,9 +469,9 @@ class BuilderTemplate extends BaseTemplate { «ENDIF» ''' - def private generateAugmentField(boolean init) ''' + def private generateAugmentField(boolean isPrivate) ''' «IF augmentField != null» - private «Map.importedName»<«Class.importedName», «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>(); + «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName», «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>(); «ENDIF» ''' diff --git a/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java b/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java new file mode 100644 index 0000000000..62190fc498 --- /dev/null +++ b/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/NestedGroupingCompilationTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2013 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.sal.java.api.generator.test; + +import static org.junit.Assert.assertTrue; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.BASE_PKG; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.COMPILER_OUTPUT_PATH; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.FS; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.GENERATOR_OUTPUT_PATH; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.NS_TEST; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.assertFilesCount; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.assertImplementsIfc; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.cleanUp; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.getSourceFiles; +import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.testCompilation; + +import java.io.File; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.HashSet; +import java.util.List; +import org.junit.Test; +import org.opendaylight.yangtools.sal.binding.model.api.Type; +import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +/** + * Test correct code generation. + * + */ +public class NestedGroupingCompilationTest extends BaseCompilationTest { + + @Test + public void testListGeneration() throws Exception { + final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "nested-grouping"); + assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir()); + final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "nested-grouping"); + assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir()); + + generateTestSources("/compilation/nested-grouping", sourcesOutputDir); + + // Test if all sources are generated + File parent = new File(sourcesOutputDir, NS_TEST); + File foo = new File(parent, "Foo.java"); + File fooBuilder = new File(parent, "FooBuilder.java"); + File testData = new File(parent, "TestData.java"); + File fooDir = new File(parent, "foo"); + assertTrue(foo.exists()); + assertTrue(fooBuilder.exists()); + assertTrue(testData.exists()); + assertTrue(fooDir.exists()); + assertFilesCount(parent, 4); + + parent = new File(parent, "foo"); + File bar = new File(parent, "Bar.java"); + assertTrue(bar.exists()); + assertFilesCount(parent, 1); + + // Test if sources are compilable + testCompilation(sourcesOutputDir, compiledOutputDir); + + ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() }); + Class fooClass = Class.forName(BASE_PKG + ".urn.opendaylight.test.rev131008.Foo", true, loader); + Class barClass = Class.forName(BASE_PKG + ".urn.opendaylight.test.rev131008.foo.Bar", true, loader); + + // Test generated 'foo' + assertTrue(fooClass.isInterface()); + assertImplementsIfc(fooClass, barClass); + + cleanUp(sourcesOutputDir, compiledOutputDir); + } + + private void generateTestSources(String resourceDirPath, File sourcesOutputDir) throws Exception { + final List sourceFiles = getSourceFiles(resourceDirPath); + final SchemaContext context = parser.parseFiles(sourceFiles); + final List types = bindingGenerator.generateTypes(context); + final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types)); + generator.generateToFile(sourcesOutputDir); + } + +} diff --git a/code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang b/code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang new file mode 100644 index 0000000000..94cf5e1cba --- /dev/null +++ b/code-generator/binding-java-api-generator/src/test/resources/compilation/nested-grouping/test.yang @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2013 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 + */ +module test { + yang-version 1; + namespace "urn:opendaylight:test"; + prefix "t"; + + revision "2013-10-08" { + } + + container foo { + grouping bar { + leaf id { + type int32; + } + } + uses bar; + } + +} diff --git a/code-generator/binding-java-api-generator/src/test/resources/compilation/typedef/mod-box.yang b/code-generator/binding-java-api-generator/src/test/resources/compilation/typedef/mod-box.yang new file mode 100644 index 0000000000..1334298696 --- /dev/null +++ b/code-generator/binding-java-api-generator/src/test/resources/compilation/typedef/mod-box.yang @@ -0,0 +1,21 @@ + +module mod-box { + yang-version 1; + namespace "urn:opendaylight:mod:box"; + prefix "box"; + + revision 2014-08-05 { + } + + container cont1 { + choice choice1 { + case case1 { + container cont2 { + leaf leaf1 { + type string; + } + } + } + } + } +} diff --git a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/BoxableType.java b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/BoxableType.java new file mode 100644 index 0000000000..258c53df07 --- /dev/null +++ b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/BoxableType.java @@ -0,0 +1,34 @@ +/* + * 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.yangtools.sal.binding.model.api; + +/** + * Implementing this interface allows an object to hold information about that if + * is generated type suitable for boxing. + * + * Example: + * choice foo-choice { + * case foo-case { + * container foo { + * ... + * } + * } + * } + * + * Suitable type have to implements ChildOf, where !(T instanceof Identifiable) and + * T does not place any structural requirements (must/when) on existence/value Foo. + */ +public interface BoxableType { + + /** + * Check if generated type is suitable for boxing. + * + * @return true if generated type is suitable for boxing, false otherwise. + */ + boolean isSuitableForBoxing(); +} diff --git a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/GeneratedType.java b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/GeneratedType.java index 0e9b51ee74..ef2fdcdd7e 100644 --- a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/GeneratedType.java +++ b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/GeneratedType.java @@ -33,7 +33,7 @@ import java.util.List; * definitions MUST be public, so there is no need to specify the scope of * visibility. */ -public interface GeneratedType extends Type, DocumentedType { +public interface GeneratedType extends Type, DocumentedType, BoxableType { /** * Returns the parent type if Generated Type is defined as enclosing type, diff --git a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilder.java b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilder.java index 9756bd2092..ea2ab258e0 100644 --- a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilder.java +++ b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilder.java @@ -12,16 +12,29 @@ import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType; /** * Generated Type Builder interface is helper interface for building and * defining the GeneratedType. - * + * * @see GeneratedType */ public interface GeneratedTypeBuilder extends GeneratedTypeBuilderBase { /** * Returns the new immutable instance of Generated Type. - * + * * @return the new immutable instance of Generated Type. */ GeneratedType toInstance(); + /** + * Set true if generated type is suitable for boxing, false otherwise. + * + * @param value + */ + public void setSuitableForBoxing(boolean value); + + /** + * Set parent for current generated type. + * + * @param parent + */ + public void setParentType(GeneratedTypeBuilder parent); } diff --git a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilderBase.java b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilderBase.java index 628029f028..70d0856fb7 100644 --- a/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilderBase.java +++ b/code-generator/binding-model-api/src/main/java/org/opendaylight/yangtools/sal/binding/model/api/type/builder/GeneratedTypeBuilderBase.java @@ -8,7 +8,6 @@ package org.opendaylight.yangtools.sal.binding.model.api.type.builder; import java.util.List; - import org.opendaylight.yangtools.sal.binding.model.api.Constant; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.common.QName; @@ -30,7 +29,7 @@ public interface GeneratedTypeBuilderBase> * Name of Enclosing Type * @return new Instance of Generated Type Builder. */ - GeneratedTOBuilder addEnclosingTransferObject(final String name); + GeneratedTOBuilder addEnclosingTransferObject(String name); /** * Adds new Enclosing Transfer Object genTOBuilder into @@ -49,7 +48,7 @@ public interface GeneratedTypeBuilderBase> * @param genTOBuilder * Name of Enclosing Type */ - T addEnclosingTransferObject(final GeneratedTOBuilder genTOBuilder); + T addEnclosingTransferObject(GeneratedTOBuilder genTOBuilder); /** * Adds String definition of comment into Method Signature definition.
@@ -59,7 +58,7 @@ public interface GeneratedTypeBuilderBase> * @param comment * Comment String. */ - T addComment(final String comment); + T addComment(String comment); /** * The method creates new AnnotationTypeBuilder containing specified package @@ -74,7 +73,7 @@ public interface GeneratedTypeBuilderBase> * Name of Annotation Type * @return new instance of Annotation Type Builder. */ - AnnotationTypeBuilder addAnnotation(final String packageName, final String name); + AnnotationTypeBuilder addAnnotation(String packageName, String name); boolean isAbstract(); @@ -96,7 +95,7 @@ public interface GeneratedTypeBuilderBase> * Type to implement * @return true if the addition of type is successful. */ - T addImplementsType(final Type genType); + T addImplementsType(Type genType); /** * Adds Constant definition and returns new Constant instance.
@@ -113,7 +112,7 @@ public interface GeneratedTypeBuilderBase> * Assigned Value * @return new Constant instance. */ - Constant addConstant(final Type type, final String name, final Object value); + Constant addConstant(Type type, String name, Object value); /** * Adds new Enumeration definition for Generated Type Builder and returns @@ -128,7 +127,7 @@ public interface GeneratedTypeBuilderBase> * Enumeration Name * @return new instance of Enumeration Builder. */ - EnumBuilder addEnumeration(final String name); + EnumBuilder addEnumeration(String name); List getMethodDefinitions(); @@ -146,7 +145,7 @@ public interface GeneratedTypeBuilderBase> * Name of Method * @return new instance of Method Signature Builder. */ - MethodSignatureBuilder addMethod(final String name); + MethodSignatureBuilder addMethod(String name); /** * Checks if GeneratedTypeBuilder contains method with name @@ -155,7 +154,7 @@ public interface GeneratedTypeBuilderBase> * @param methodName * is method name */ - boolean containsMethod(final String methodName); + boolean containsMethod(String methodName); List getProperties(); @@ -169,7 +168,7 @@ public interface GeneratedTypeBuilderBase> * Name of Property * @return new instance of Generated Property Builder. */ - GeneratedPropertyBuilder addProperty(final String name); + GeneratedPropertyBuilder addProperty(String name); /** * Check whether GeneratedTOBuilder contains property with name @@ -179,7 +178,7 @@ public interface GeneratedTypeBuilderBase> * of property which existance is checked * @return true if property name exists in list of properties. */ - boolean containsProperty(final String name); + boolean containsProperty(String name); /** * Set a string that contains a human-readable textual description of type diff --git a/code-generator/binding-test-model/pom.xml b/code-generator/binding-test-model/pom.xml new file mode 100644 index 0000000000..106c90439e --- /dev/null +++ b/code-generator/binding-test-model/pom.xml @@ -0,0 +1,67 @@ + + + + + + org.opendaylight.yangtools + binding-generator + 0.6.2-SNAPSHOT + + 4.0.0 + + + + org.opendaylight.yangtools + yang-binding + + + org.opendaylight.yangtools.model + yang-ext + + + + binding-test-model + + + + org.opendaylight.yangtools + yang-maven-plugin + + + + generate-sources + + + + + + org.opendaylight.yangtools.maven.sal.api.gen.plugin.CodeGeneratorImpl + + + target/generated-sources/sal + + + + true + + + + + + + + scm:git:ssh://git.opendaylight.org:29418/controller.git + scm:git:ssh://git.opendaylight.org:29418/controller.git + HEAD + https://wiki.opendaylight.org/view/OpenDaylight_Controller:MD-SAL + + + diff --git a/code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java b/code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java new file mode 100644 index 0000000000..0d3e7e89ce --- /dev/null +++ b/code-generator/binding-test-model/src/main/java/org/opendaylight/controller/md/sal/test/model/util/ListsBindingUtils.java @@ -0,0 +1,88 @@ +/* + * 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.test.model.util; + +import com.google.common.collect.ImmutableList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUsesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUsesKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.TopBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.NestedListKey; +import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; + +import java.util.Arrays; + +public class ListsBindingUtils { + + private static final InstanceIdentifier TOP_PATH = InstanceIdentifier.create(Top.class); + + private ListsBindingUtils() { + throw new UnsupportedOperationException(); + } + + public static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + public static final TopLevelListKey TOP_BAR_KEY = new TopLevelListKey("bar"); + public static final ListViaUsesKey USES_ONE_KEY = new ListViaUsesKey("one"); + public static final ListViaUsesKey USES_TWO_KEY = new ListViaUsesKey("two"); + + + public static InstanceIdentifier path(final TopLevelListKey key) { + return TOP_PATH.child(TopLevelList.class, key); + } + + public static InstanceIdentifier path(final TopLevelListKey top,final NestedListKey nested) { + return path(top).child(NestedList.class, nested); + } + + public static InstanceIdentifier path(final TopLevelListKey top,final ListViaUsesKey uses) { + return path(top).augmentation(TreeComplexUsesAugment.class).child(ListViaUses.class, uses); + } + + public static > InstanceIdentifier path(final TopLevelListKey key, final Class augmentation) { + return path(key).augmentation(augmentation); + } + + public static Top top(final TopLevelList... listItems) { + return new TopBuilder().setTopLevelList(Arrays.asList(listItems)).build(); + } + + public static TopLevelList topLevelList(final TopLevelListKey key) { + return new TopLevelListBuilder().setKey(key).build(); + } + + public static TopLevelList topLevelList(final TopLevelListKey key, final TreeComplexUsesAugment augment) { + TopLevelListBuilder builder = new TopLevelListBuilder().setKey(key); + builder.addAugmentation(TreeComplexUsesAugment.class, augment); + return builder.build(); + } + + public static TreeComplexUsesAugment complexUsesAugment(final ListViaUsesKey... keys) { + ImmutableList.Builder listViaUses = ImmutableList. builder(); + for (ListViaUsesKey key : keys) { + listViaUses.add(new ListViaUsesBuilder().setKey(key).build()); + } + return new TreeComplexUsesAugmentBuilder().setListViaUses(listViaUses.build()).build(); + } + + public static TreeLeafOnlyUsesAugment leafOnlyUsesAugment(String leafFromGroupingValue) { + + return new TreeLeafOnlyUsesAugmentBuilder().setLeafFromGrouping(leafFromGroupingValue).build(); + } + +} diff --git a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang new file mode 100644 index 0000000000..6d155becc7 --- /dev/null +++ b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang @@ -0,0 +1,111 @@ +module opendaylight-yangtools-augment-test { + + namespace "urn:opendaylight:params:xml:ns:yang:yangtools:test:augment"; + prefix aug-test; + + import opendaylight-yangtools-binding-test { + prefix test; + } + import yang-ext { + prefix ext; + } + + description + "This module contains a collection of YANG augmentations used for + some test cases."; + + revision 2014-07-09 { + description + "Test model for testing data broker with nested lists."; + } + + grouping leaf-from-grouping { + leaf leaf-from-grouping { + type string; + } + } + + grouping complex-from-grouping { + container container-with-uses { + uses leaf-from-grouping; + } + list list-via-uses { + key "name"; + leaf name { + type string; + } + } + + } + + augment "/test:top/test:top-level-list" { + ext:augment-identifier tree-leaf-only-uses-augment; + uses leaf-from-grouping; + } + + augment "/test:put-top/test:input/test:top-level-list" { + ext:augment-identifier rpc-leaf-only-uses-augment; + uses leaf-from-grouping; + } + + augment "/test:top/test:top-level-list" { + ext:augment-identifier tree-complex-uses-augment; + uses complex-from-grouping; + } + + augment "/test:put-top/test:input/test:top-level-list" { + ext:augment-identifier rpc-complex-uses-augment; + uses complex-from-grouping; + } + + augment "/test:top/test:top-level-list" { + ext:augment-identifier tree-leaf-only-augment; + + leaf simple-value { + type string; + } + } + + augment "/test:top/test:top-level-list" { + ext:augment-identifier tree-second-leaf-only-augment; + + leaf second-simple-value { + type string; + } + } + + augment "/test:put-top/test:input/test:top-level-list" { + ext:augment-identifier rpc-leaf-only-augment; + + leaf simple-value { + type string; + } + } + + augment "/test:put-top/test:input/test:top-level-list" { + ext:augment-identifier rpc-second-leaf-only-augment; + + leaf second-simple-value { + type string; + } + } + + augment "/test:top/test:top-level-list/test:choice-in-list" { + case simple-via-uses { + uses leaf-from-grouping; + } + case complex-via-uses { + uses complex-from-grouping; + } + } + + augment "/test:put-top/test:input/test:top-level-list/test:choice-in-list" { + case simple-via-uses { + uses leaf-from-grouping; + } + case complex-via-uses-with-different-name { + uses complex-from-grouping; + } + } + +} diff --git a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang new file mode 100644 index 0000000000..c5e5ce23b7 --- /dev/null +++ b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-binding-test.yang @@ -0,0 +1,86 @@ +module opendaylight-yangtools-binding-test { + + namespace "urn:opendaylight:params:xml:ns:yang:yangtools:test:binding"; + prefix list-test; + + + description + "This module contains a collection of YANG definitions used for + some test cases."; + + revision 2014-07-01 { + description + "Test model for testing data broker with nested lists."; + } + + grouping two-level-list { + list top-level-list { + description + "Top Level List"; + key "name"; + leaf name { + type string; + } + + choice choice-in-list { + case simple-case { + leaf simple { + type string; + } + } + } + + list nested-list { + key "name"; + leaf name { + type string; + } + leaf type { + type string; + mandatory true; + description + "Mandatory type of list."; + } + ordered-by user; + description + "A list of service functions that compose the service chain"; + } + } + leaf-list top-level-leaf-list { + type string; + } + } + + grouping choice { + choice identifier { + case simple { + container simple-id { + leaf id { + type int32; + } + } + } + case extended { + container extended-id { + leaf id { + type string; + } + } + } + } + } + + container top { + uses two-level-list; + } + + container choice-container { + uses choice; + } + + rpc put-top { + input { + uses two-level-list; + } + } +} diff --git a/code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java b/code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java index f1864ab1b7..d48046a48b 100644 --- a/code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java +++ b/code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java @@ -1299,9 +1299,10 @@ public final class TypeProviderImpl implements TypeProvider { } typeDefinitionsConcreteDepth.add(unsortedTypeDefinition); } - // keys are in ascending order - for (Map.Entry>> entry : typeDefinitionsDepths.entrySet()) { - sortedTypeDefinition.addAll(entry.getValue()); + + // SortedMap guarantees order corresponding to keys in ascending order + for (List> v : typeDefinitionsDepths.values()) { + sortedTypeDefinition.addAll(v); } return sortedTypeDefinition; diff --git a/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend b/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend index d8c4959296..f40cbcaa3d 100644 --- a/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend +++ b/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend @@ -975,9 +975,9 @@ class GeneratorImpl { «ENDIF» - «IF !path.path.empty» + «IF path.pathArguments.iterator.hasNext»

XML example

- «nodes.xmlExample(path.path.last.nodeType,path)» + «nodes.xmlExample(path.pathArguments.last.nodeType,path)» «ENDIF» «FOR childNode : containers» @@ -1045,8 +1045,8 @@ class GeneratorImpl { def header(int level,YangInstanceIdentifier name) ''' - - «FOR cmp : name.path SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR» + + «FOR cmp : name.pathArguments SEPARATOR "/"»«cmp.nodeType.localName»«ENDFOR» ''' @@ -1152,7 +1152,7 @@ class GeneratorImpl { } def CharSequence localLink(YangInstanceIdentifier identifier, CharSequence text) ''' - «text» + «text» ''' @@ -1181,7 +1181,7 @@ class GeneratorImpl { append(currentModule.name) append(":") var previous = false; - for(arg : identifier.path) { + for(arg : identifier.pathArguments) { if(previous) append("/") append(arg.nodeType.localName); previous = true; diff --git a/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java b/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java index 1208f190f3..1a1052baa1 100644 --- a/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java +++ b/code-generator/maven-sal-api-gen-plugin/src/test/java/org/opendaylight/yangtools/yang/unified/doc/generator/maven/YangModuleInfoCompilationTest.java @@ -82,13 +82,9 @@ public class YangModuleInfoCompilationTest { testCompilation(sourcesOutputDir, compiledOutputDir); // Create URLClassLoader - File[] roots = File.listRoots(); - URL[] urls = new URL[roots.length + 1]; - for (int i = 0; i < roots.length; i++) { - urls[i] = roots[i].toURI().toURL(); - - } - urls[roots.length] = compiledOutputDir.toURI().toURL(); + URL[] urls = new URL[2]; + urls[0] = compiledOutputDir.toURI().toURL(); + urls[1] = new File(System.getProperty("user.dir")).toURI().toURL(); ClassLoader loader = new URLClassLoader(urls); // Load class @@ -176,13 +172,16 @@ public class YangModuleInfoCompilationTest { private static List getSourceFiles(String path) throws Exception { final URI resPath = YangModuleInfoCompilationTest.class.getResource(path).toURI(); final File sourcesDir = new File(resPath); + final URI currentDir = new File(System.getProperty("user.dir")).toURI(); if (sourcesDir.exists()) { final List sourceFiles = new ArrayList<>(); final File[] fileArray = sourcesDir.listFiles(); if (fileArray == null) { throw new IllegalArgumentException("Unable to locate files in " + sourcesDir); } - sourceFiles.addAll(Arrays.asList(fileArray)); + for (File sourceFile : fileArray) { + sourceFiles.add(new File(currentDir.relativize(sourceFile.toURI()).toString())); + } return sourceFiles; } else { throw new FileNotFoundException("Testing files were not found(" + sourcesDir.getName() + ")"); diff --git a/code-generator/pom.xml b/code-generator/pom.xml index 4cdf7c23c7..fc239681b5 100644 --- a/code-generator/pom.xml +++ b/code-generator/pom.xml @@ -30,6 +30,7 @@ binding-java-api-generator binding-type-provider maven-sal-api-gen-plugin + binding-test-model binding-data-codec @@ -43,6 +44,11 @@
+ + org.opendaylight.yangtools + binding-test-model + ${project.version} + org.opendaylight.yangtools binding-model-api diff --git a/common/features/pom.xml b/common/features/pom.xml index a3de6e5b78..46c91492d0 100644 --- a/common/features/pom.xml +++ b/common/features/pom.xml @@ -7,248 +7,260 @@ terms of the Eclipse Public License v1.0 which accompanies this distribution, and is available at http://www.eclipse.org/legal/epl-v10.html --> - - 4.0.0 - - org.opendaylight.yangtools - yangtools-parent - 0.6.2-SNAPSHOT - ../parent/pom.xml - - features-yangtools - jar - - features.xml - - - - - src/main/resources - true - - - - - org.apache.karaf.tooling - karaf-maven-plugin - ${karaf.version} - true - - - features-create-kar - - features-create-kar - - - ${project.build.directory}/classes/${features.file} - - - - - - - org.apache.maven.plugins - maven-resources-plugin - - - filter - generate-resources - - resources - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - attach-artifacts - package - - attach-artifact - - - - - ${project.build.directory}/classes/${features.file} - xml - features - - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - org.opendaylight.yangtools:features-test - - - - - - - - org.opendaylight.yangtools.model - ietf-inet-types - - - org.opendaylight.yangtools.model - ietf-yang-types - - - org.opendaylight.yangtools.model - yang-ext - - - org.opendaylight.yangtools.model - opendaylight-l2-types - - - org.opendaylight.yangtools.model - ietf-topology - - - org.opendaylight.yangtools.thirdparty - antlr4-runtime-osgi-nohead - compile - - - org.opendaylight.yangtools - yang-data-api - - - org.opendaylight.yangtools - yang-data-composite-node - - - org.opendaylight.yangtools - yang-data-impl - - - org.opendaylight.yangtools - yang-data-operations - - - org.opendaylight.yangtools - yang-data-util - - - org.opendaylight.yangtools - yang-model-api - - - org.opendaylight.yangtools - yang-model-util - - - org.opendaylight.yangtools - yang-parser-impl - - - org.opendaylight.yangtools - yang-parser-api - - - org.opendaylight.yangtools - yang-binding - - - org.opendaylight.yangtools - util - - - org.opendaylight.yangtools - concepts - - - org.opendaylight.yangtools - yang-common - - - org.opendaylight.yangtools - util - - - org.opendaylight.yangtools - object-cache-api - - - org.opendaylight.yangtools - object-cache-guava - - - org.opendaylight.yangtools - object-cache-noop - - - org.opendaylight.yangtools - binding-generator-api - - - org.opendaylight.yangtools - binding-generator-impl - - - org.opendaylight.yangtools - binding-generator-spi - - - org.opendaylight.yangtools - binding-generator-util - - - org.opendaylight.yangtools - binding-model-api - - - org.opendaylight.yangtools - binding-type-provider - - - org.opendaylight.yangtools - yang-model-api - - - org.opendaylight.yangtools - yang-model-util - - - org.opendaylight.yangtools - yang-parser-api - - - org.opendaylight.yangtools - restconf-client-api - - - org.opendaylight.yangtools - restconf-client-impl - - - org.opendaylight.yangtools - restconf-common - - - org.opendaylight.yangtools - binding-data-codec - - - org.opendaylight.yangtools.thirdparty - xtend-lib-osgi - compile - - - - org.opendaylight.yangtools - features-test - test - - + + 4.0.0 + + org.opendaylight.yangtools + yangtools-parent + 0.6.2-SNAPSHOT + ../parent/pom.xml + + features-yangtools + jar + + features.xml + + + + + src/main/resources + true + + + + + org.apache.karaf.tooling + karaf-maven-plugin + ${karaf.version} + true + + + features-create-kar + + features-create-kar + + + ${project.build.directory}/classes/${features.file} + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + filter + generate-resources + + resources + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + attach-artifacts + package + + attach-artifact + + + + + ${project.build.directory}/classes/${features.file} + xml + features + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + org.opendaylight.yangtools:features-test + + + + + + + + org.opendaylight.yangtools.model + ietf-inet-types + + + org.opendaylight.yangtools.model + ietf-yang-types + + + org.opendaylight.yangtools.model + ietf-yang-types-20130715 + + + org.opendaylight.yangtools.model + yang-ext + + + org.opendaylight.yangtools.model + opendaylight-l2-types + + + org.opendaylight.yangtools.model + ietf-topology + + + org.opendaylight.yangtools.thirdparty + antlr4-runtime-osgi-nohead + compile + + + org.opendaylight.yangtools + yang-data-api + + + org.opendaylight.yangtools + yang-data-composite-node + + + org.opendaylight.yangtools + yang-data-impl + + + org.opendaylight.yangtools + yang-data-operations + + + org.opendaylight.yangtools + yang-data-util + + + org.opendaylight.yangtools + yang-data-codec-gson + + + org.opendaylight.yangtools + yang-model-api + + + org.opendaylight.yangtools + yang-model-util + + + org.opendaylight.yangtools + yang-parser-impl + + + org.opendaylight.yangtools + yang-parser-api + + + org.opendaylight.yangtools + yang-binding + + + org.opendaylight.yangtools + util + + + org.opendaylight.yangtools + concepts + + + org.opendaylight.yangtools + yang-common + + + org.opendaylight.yangtools + util + + + org.opendaylight.yangtools + object-cache-api + + + org.opendaylight.yangtools + object-cache-guava + + + org.opendaylight.yangtools + object-cache-noop + + + org.opendaylight.yangtools + binding-generator-api + + + org.opendaylight.yangtools + binding-generator-impl + + + org.opendaylight.yangtools + binding-generator-spi + + + org.opendaylight.yangtools + binding-generator-util + + + org.opendaylight.yangtools + binding-model-api + + + org.opendaylight.yangtools + binding-type-provider + + + org.opendaylight.yangtools + yang-model-api + + + org.opendaylight.yangtools + yang-model-util + + + org.opendaylight.yangtools + yang-parser-api + + + org.opendaylight.yangtools + restconf-client-api + + + org.opendaylight.yangtools + restconf-client-impl + + + org.opendaylight.yangtools + restconf-common + + + org.opendaylight.yangtools + binding-data-codec + + + org.opendaylight.yangtools.thirdparty + xtend-lib-osgi + compile + + + com.google.code.gson + gson + + + + + org.opendaylight.yangtools + features-test + test + + diff --git a/common/features/src/main/resources/features.xml b/common/features/src/main/resources/features.xml index 58b3ab1779..45f1a8de1b 100644 --- a/common/features/src/main/resources/features.xml +++ b/common/features/src/main/resources/features.xml @@ -23,6 +23,7 @@ odl-yangtools-binding mvn:org.opendaylight.yangtools.model/ietf-inet-types/${ietf.inet.types.version} mvn:org.opendaylight.yangtools.model/ietf-yang-types/${ietf.yang.types.version} + mvn:org.opendaylight.yangtools.model/ietf-yang-types-20130715/${ietf.yang.types.20130715.version} mvn:org.opendaylight.yangtools.model/yang-ext/${yang.ext.version} mvn:org.opendaylight.yangtools.model/opendaylight-l2-types/${opendaylight.l2.types.version} mvn:org.opendaylight.yangtools.model/ietf-topology/${ietf.topology.version} @@ -33,14 +34,18 @@ mvn:org.opendaylight.yangtools.thirdparty/antlr4-runtime-osgi-nohead/${antlr4.version} mvn:commons-io/commons-io/${commons.io.version} mvn:org.opendaylight.yangtools/yang-data-api/${project.version} + mvn:org.opendaylight.yangtools/yang-data-composite-node/${project.version} mvn:org.opendaylight.yangtools/yang-data-impl/${project.version} mvn:org.opendaylight.yangtools/yang-data-operations/${project.version} - mvn:org.opendaylight.yangtools/yang-data-composite-node/${project.version} mvn:org.opendaylight.yangtools/yang-data-util/${project.version} mvn:org.opendaylight.yangtools/yang-model-api/${project.version} mvn:org.opendaylight.yangtools/yang-model-util/${project.version} - mvn:org.opendaylight.yangtools/yang-parser-impl/${project.version} mvn:org.opendaylight.yangtools/yang-parser-api/${project.version} + mvn:org.opendaylight.yangtools/yang-parser-impl/${project.version} + + + mvn:com.google.code.gson/gson/${gson.version} + mvn:org.opendaylight.yangtools/yang-data-codec-gson/${project.version} diff --git a/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/ObjectCacheFactory.java b/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/ObjectCacheFactory.java index dd3e53bbd0..a18f6edc17 100644 --- a/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/ObjectCacheFactory.java +++ b/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/ObjectCacheFactory.java @@ -7,30 +7,32 @@ */ package org.opendaylight.yangtools.objcache; +import com.google.common.base.Preconditions; + import javax.annotation.Nonnull; +import javax.annotation.concurrent.GuardedBy; import org.opendaylight.yangtools.objcache.impl.StaticObjectCacheBinder; import org.opendaylight.yangtools.objcache.spi.IObjectCacheFactory; import org.opendaylight.yangtools.objcache.spi.NoopObjectCacheBinder; -import com.google.common.base.Preconditions; - /** * Point of entry for acquiring an {@link ObjectCache} instance. */ public final class ObjectCacheFactory { - private static IObjectCacheFactory FACTORY; + private static volatile IObjectCacheFactory factory; + @GuardedBy("this") private static synchronized IObjectCacheFactory initialize() { // Double-check under lock - if (FACTORY != null) { - return FACTORY; + IObjectCacheFactory f = factory; + if (f != null) { + return f; } - IObjectCacheFactory f; try { f = StaticObjectCacheBinder.getInstance().getProductCacheFactory(); - FACTORY = f; + factory = f; } catch (NoClassDefFoundError e) { f = NoopObjectCacheBinder.INSTANCE.getProductCacheFactory(); } @@ -39,7 +41,7 @@ public final class ObjectCacheFactory { } public static synchronized void reset() { - FACTORY = null; + factory = null; } /** @@ -50,7 +52,7 @@ public final class ObjectCacheFactory { * @return Object cache instance. */ public static ObjectCache getObjectCache(@Nonnull final Class objClass) { - IObjectCacheFactory f = FACTORY; + IObjectCacheFactory f = factory; if (f == null) { f = initialize(); } diff --git a/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/spi/AbstractObjectCache.java b/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/spi/AbstractObjectCache.java index aeef1ee5cf..3e09e967ca 100644 --- a/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/spi/AbstractObjectCache.java +++ b/common/object-cache-api/src/main/java/org/opendaylight/yangtools/objcache/spi/AbstractObjectCache.java @@ -7,20 +7,18 @@ */ package org.opendaylight.yangtools.objcache.spi; +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.FinalizableReferenceQueue; +import com.google.common.base.FinalizableSoftReference; +import com.google.common.base.Preconditions; +import com.google.common.cache.Cache; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; - import org.opendaylight.yangtools.concepts.ProductAwareBuilder; import org.opendaylight.yangtools.objcache.ObjectCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.FinalizableReferenceQueue; -import com.google.common.base.FinalizableSoftReference; -import com.google.common.base.Preconditions; -import com.google.common.cache.Cache; - /** * Abstract object cache implementation. This implementation takes care * of interacting with the user and manages interaction with the Garbage @@ -53,7 +51,7 @@ public abstract class AbstractObjectCache implements ObjectCache { * to be on the lookout for WeakKeys, as we cannot pass them * directly to productEquals(). */ - if (obj != null && obj instanceof SoftKey) { + if (obj instanceof SoftKey) { obj = ((SoftKey)obj).get(); } diff --git a/common/parent/pom.xml b/common/parent/pom.xml index 76a2e10875..15e887eb44 100644 --- a/common/parent/pom.xml +++ b/common/parent/pom.xml @@ -35,6 +35,7 @@ 2013.10.21.2-SNAPSHOT 2010.09.24.4-SNAPSHOT 2010.09.24.4-SNAPSHOT + 2013.07.15.1-SNAPSHOT 2013.10.19.1-SNAPSHOT 3.0.1 http://nexus.opendaylight.org/content @@ -209,6 +210,11 @@ yang-data-impl 0.6.2-SNAPSHOT + + org.opendaylight.yangtools + yang-data-codec-gson + 0.6.2-SNAPSHOT + org.opendaylight.yangtools yang-model-api @@ -254,6 +260,11 @@ ietf-yang-types 2010.09.24.4-SNAPSHOT + + org.opendaylight.yangtools.model + ietf-yang-types-20130715 + 2013.07.15.1-SNAPSHOT + org.opendaylight.yangtools.model ietf-restconf @@ -454,6 +465,11 @@ ${sonar-jacoco-listeners.version} test + + org.slf4j + slf4j-simple + test + @@ -839,7 +855,7 @@ opendaylight-mirror opendaylight-mirror - http://nexus.opendaylight.org/content/groups/public/ + ${nexusproxy}/groups/public/ false @@ -853,7 +869,7 @@ opendaylight-snapshot opendaylight-snapshot - http://nexus.opendaylight.org/content/repositories/opendaylight.snapshot/ + ${nexusproxy}/repositories/opendaylight.snapshot/ true diff --git a/common/util/pom.xml b/common/util/pom.xml index 5d185efe51..f3502891ba 100644 --- a/common/util/pom.xml +++ b/common/util/pom.xml @@ -61,10 +61,6 @@ true - - org.opendaylight.yangtools.util, - org.opendaylight.yangtools.util.concurrent - java-concurrent-hash-trie-map;inline=true diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatsTracker.java b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatsTracker.java new file mode 100644 index 0000000000..45ebd01306 --- /dev/null +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/DurationStatsTracker.java @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util; + +import static java.util.concurrent.TimeUnit.MICROSECONDS; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.NANOSECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.Date; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import com.google.common.util.concurrent.AtomicDouble; + +/** + * Class that calculates and tracks time duration statistics. + * + * @author Thomas Pantelis + */ +public class DurationStatsTracker { + + private final AtomicLong totalDurations = new AtomicLong(); + private final AtomicLong longestDuration = new AtomicLong(); + private volatile long timeOfLongestDuration; + private final AtomicLong shortestDuration = new AtomicLong(Long.MAX_VALUE); + private volatile long timeOfShortestDuration; + private final AtomicDouble averageDuration = new AtomicDouble(); + + /** + * Add a duration to track. + * + * @param duration the duration in nanoseconds. + */ + public void addDuration(long duration) { + + double currentAve = averageDuration.get(); + long currentTotal = totalDurations.get(); + + long newTotal = currentTotal + 1; + + // Calculate moving cumulative average. + double newAve = currentAve * currentTotal / newTotal + duration / newTotal; + + averageDuration.compareAndSet(currentAve, newAve); + totalDurations.compareAndSet(currentTotal, newTotal); + + long longest = longestDuration.get(); + if( duration > longest ) { + if(longestDuration.compareAndSet( longest, duration )) { + timeOfLongestDuration = System.currentTimeMillis(); + } + } + + long shortest = shortestDuration.get(); + if( duration < shortest ) { + if(shortestDuration.compareAndSet( shortest, duration )) { + timeOfShortestDuration = System.currentTimeMillis(); + } + } + } + + /** + * Returns the total number of tracked durations. + */ + public long getTotalDurations() { + return totalDurations.get(); + } + + /** + * Returns the longest duration in nanoseconds. + */ + public long getLongestDuration() { + return longestDuration.get(); + } + + /** + * Returns the shortest duration in nanoseconds. + */ + public long getShortestDuration() { + long shortest = shortestDuration.get(); + return shortest < Long.MAX_VALUE ? shortest : 0; + } + + /** + * Returns the average duration in nanoseconds. + */ + public double getAverageDuration() { + return averageDuration.get(); + } + + /** + * Returns the time stamp of the longest duration. + */ + public long getTimeOfLongestDuration() { + return timeOfLongestDuration; + } + + /** + * Returns the time stamp of the shortest duration. + */ + public long getTimeOfShortestDuration() { + return timeOfShortestDuration; + } + + /** + * Resets all statistics back to their defaults. + */ + public void reset() { + totalDurations.set(0); + longestDuration.set(0); + timeOfLongestDuration = 0; + shortestDuration.set(Long.MAX_VALUE); + timeOfShortestDuration = 0; + averageDuration.set(0.0); + } + + /** + * Returns the average duration as a displayable String with units, e.g. "12.34 ms". + */ + public String getDisplayableAverageDuration() { + return formatDuration(getAverageDuration(), 0); + } + + /** + * Returns the shortest duration as a displayable String with units and the date/time at + * which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24". + */ + public String getDisplayableShortestDuration() { + return formatDuration(getShortestDuration(), getTimeOfShortestDuration()); + } + + /** + * Returns the longest duration as a displayable String with units and the date/time at + * which it occurred, e.g. "12.34 ms at 08/02/2014 12:30:24". + */ + public String getDisplayableLongestDuration() { + return formatDuration(getLongestDuration(), getTimeOfLongestDuration()); + } + + private String formatDuration(double duration, long timeStamp) { + TimeUnit unit = chooseUnit((long)duration); + double value = duration / NANOSECONDS.convert(1, unit); + return timeStamp > 0 ? + String.format("%.4g %s at %3$tD %3$tT", value, abbreviate(unit), new Date(timeStamp)) : + String.format("%.4g %s", value, abbreviate(unit)); + } + + private static TimeUnit chooseUnit(long nanos) { + if(SECONDS.convert(nanos, NANOSECONDS) > 0) { + return SECONDS; + } + + if(MILLISECONDS.convert(nanos, NANOSECONDS) > 0) { + return MILLISECONDS; + } + + if(MICROSECONDS.convert(nanos, NANOSECONDS) > 0) { + return MICROSECONDS; + } + + return NANOSECONDS; + } + + private static String abbreviate(TimeUnit unit) { + switch(unit) { + case NANOSECONDS: + return "ns"; + case MICROSECONDS: + return "\u03bcs"; // μs + case MILLISECONDS: + return "ms"; + case SECONDS: + return "s"; + default: + return ""; + } + } +} diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java index 4936efaba1..a7dd4af009 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CachedThreadPoolExecutor.java @@ -14,8 +14,6 @@ import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicLong; - import com.google.common.base.Objects; import com.google.common.base.Objects.ToStringHelper; import com.google.common.base.Preconditions; @@ -33,8 +31,6 @@ public class CachedThreadPoolExecutor extends ThreadPoolExecutor { private static final long IDLE_TIMEOUT_IN_SEC = 60L; - private final AtomicLong largestBackingQueueSize = new AtomicLong( 0 ); - private final ExecutorQueue executorQueue; private final String threadPrefix; @@ -76,22 +72,28 @@ public class CachedThreadPoolExecutor extends ThreadPoolExecutor { executorQueue = (ExecutorQueue)super.getQueue(); rejectedTaskHandler = new RejectedTaskHandler( - executorQueue.getBackingQueue(), largestBackingQueueSize ); + executorQueue.getBackingQueue(), CountingRejectedExecutionHandler.newAbortPolicy() ); super.setRejectedExecutionHandler( rejectedTaskHandler ); } @Override public void setRejectedExecutionHandler( RejectedExecutionHandler handler ) { + Preconditions.checkNotNull( handler ); rejectedTaskHandler.setDelegateRejectedExecutionHandler( handler ); } + @Override + public RejectedExecutionHandler getRejectedExecutionHandler(){ + return rejectedTaskHandler.getDelegateRejectedExecutionHandler(); + } + @Override public BlockingQueue getQueue(){ return executorQueue.getBackingQueue(); } public long getLargestQueueSize() { - return largestBackingQueueSize.get(); + return ((TrackingLinkedBlockingQueue)executorQueue.getBackingQueue()).getLargestQueueSize(); } protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) { @@ -129,7 +131,7 @@ public class CachedThreadPoolExecutor extends ThreadPoolExecutor { private final LinkedBlockingQueue backingQueue; ExecutorQueue( int maxBackingQueueSize ) { - backingQueue = new LinkedBlockingQueue<>( maxBackingQueueSize ); + backingQueue = new TrackingLinkedBlockingQueue<>( maxBackingQueueSize ); } LinkedBlockingQueue getBackingQueue() { @@ -189,20 +191,23 @@ public class CachedThreadPoolExecutor extends ThreadPoolExecutor { private static class RejectedTaskHandler implements RejectedExecutionHandler { private final LinkedBlockingQueue backingQueue; - private final AtomicLong largestBackingQueueSize; private volatile RejectedExecutionHandler delegateRejectedExecutionHandler; RejectedTaskHandler( LinkedBlockingQueue backingQueue, - AtomicLong largestBackingQueueSize ) { + RejectedExecutionHandler delegateRejectedExecutionHandler ) { this.backingQueue = backingQueue; - this.largestBackingQueueSize = largestBackingQueueSize; + this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler; } void setDelegateRejectedExecutionHandler( - RejectedExecutionHandler delegateRejectedExecutionHandler ){ + RejectedExecutionHandler delegateRejectedExecutionHandler ) { this.delegateRejectedExecutionHandler = delegateRejectedExecutionHandler; } + RejectedExecutionHandler getDelegateRejectedExecutionHandler(){ + return delegateRejectedExecutionHandler; + } + @Override public void rejectedExecution( Runnable task, ThreadPoolExecutor executor ) { if( executor.isShutdown() ) { @@ -210,19 +215,7 @@ public class CachedThreadPoolExecutor extends ThreadPoolExecutor { } if( !backingQueue.offer( task ) ) { - if( delegateRejectedExecutionHandler != null ) { - delegateRejectedExecutionHandler.rejectedExecution( task, executor ); - } else { - throw new RejectedExecutionException( - "All threads are in use and the queue is full" ); - } - } - - largestBackingQueueSize.incrementAndGet(); - long size = backingQueue.size(); - long largest = largestBackingQueueSize.get(); - if( size > largest ) { - largestBackingQueueSize.compareAndSet( largest, size ); + delegateRejectedExecutionHandler.rejectedExecution( task, executor ); } } } diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java new file mode 100644 index 0000000000..ab010c964d --- /dev/null +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandler.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util.concurrent; + +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.atomic.AtomicLong; + +import org.opendaylight.yangtools.util.ExecutorServiceUtil; + +import com.google.common.base.Preconditions; + +/** + * A RejectedExecutionHandler that delegates to a backing RejectedExecutionHandler and counts the + * number of rejected tasks. + * + * @author Thomas Pantelis + */ +public class CountingRejectedExecutionHandler implements RejectedExecutionHandler { + + private final RejectedExecutionHandler delegate; + private final AtomicLong rejectedTaskCounter = new AtomicLong(); + + /** + * Constructor. + * + * @param delegate the backing RejectedExecutionHandler. + */ + public CountingRejectedExecutionHandler( RejectedExecutionHandler delegate ) { + this.delegate = Preconditions.checkNotNull( delegate ); + } + + @Override + public void rejectedExecution( Runnable task, ThreadPoolExecutor executor ) { + rejectedTaskCounter.incrementAndGet(); + delegate.rejectedExecution( task, executor ); + } + + /** + * Returns the rejected task count. + */ + public long getRejectedTaskCount(){ + return rejectedTaskCounter.get(); + } + + /** + * Returns s counting handler for rejected tasks that runs the rejected task directly in the + * calling thread of the execute method, unless the executor has been shut down, in which case + * the task is discarded. + */ + public static CountingRejectedExecutionHandler newCallerRunsPolicy() { + return new CountingRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); + } + + /** + * Returns a counting handler for rejected tasks that throws a RejectedExecutionException. + */ + public static CountingRejectedExecutionHandler newAbortPolicy() { + return new CountingRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy() ); + } + + /** + * Returns a counting handler for rejected tasks that that blocks on the + * {@link ThreadPoolExecutor}'s backing queue until it can add the task to the queue. + */ + public static CountingRejectedExecutionHandler newCallerWaitsPolicy() { + return new CountingRejectedExecutionHandler( ExecutorServiceUtil.waitInQueueExecutionHandler() ); + } +} diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java index b7549eb24e..bf92ca5d9b 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/FastThreadPoolExecutor.java @@ -8,7 +8,6 @@ package org.opendaylight.yangtools.util.concurrent; -import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -70,7 +69,7 @@ public class FastThreadPoolExecutor extends ThreadPoolExecutor { // reached, subsequent tasks will be queued. If the queue is full, tasks will be rejected. super( maximumPoolSize, maximumPoolSize, keepAliveTime, unit, - new LinkedBlockingQueue( maximumQueueSize ) ); + new TrackingLinkedBlockingQueue( maximumQueueSize ) ); this.threadPrefix = threadPrefix; this.maximumQueueSize = maximumQueueSize; @@ -82,6 +81,12 @@ public class FastThreadPoolExecutor extends ThreadPoolExecutor { // Need to specifically configure core threads to timeout. allowCoreThreadTimeOut( true ); } + + setRejectedExecutionHandler( CountingRejectedExecutionHandler.newAbortPolicy() ); + } + + public long getLargestQueueSize() { + return ((TrackingLinkedBlockingQueue)getQueue()).getLargestQueueSize(); } protected ToStringHelper addToStringAttributes( ToStringHelper toStringHelper ) { @@ -96,6 +101,7 @@ public class FastThreadPoolExecutor extends ThreadPoolExecutor { .add( "Largest Thread Pool Size", getLargestPoolSize() ) .add( "Max Thread Pool Size", getMaximumPoolSize() ) .add( "Current Queue Size", getQueue().size() ) + .add( "Largest Queue Size", getLargestQueueSize() ) .add( "Max Queue Size", maximumQueueSize ) .add( "Active Thread Count", getActiveCount() ) .add( "Completed Task Count", getCompletedTaskCount() ) diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java new file mode 100644 index 0000000000..a8edd9ab46 --- /dev/null +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/ListenerNotificationQueueStats.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util.concurrent; + +import java.beans.ConstructorProperties; + +/** + * Class used by the {@link QueuedNotificationManager} that contains a snapshot of notification + * queue statistics for a listener. + * + * @author Thomas Pantelis + * @see QueuedNotificationManager + */ +public class ListenerNotificationQueueStats { + + private final String listenerClassName; + private final int currentQueueSize; + + @ConstructorProperties({"listenerClassName","currentQueueSize"}) + public ListenerNotificationQueueStats( String listenerClassName, int currentQueueSize ) { + this.listenerClassName = listenerClassName; + this.currentQueueSize = currentQueueSize; + } + + /** + * Returns the name of the listener class. + */ + public String getListenerClassName(){ + return listenerClassName; + } + + /** + * Returns the current notification queue size. + */ + public int getCurrentQueueSize(){ + return currentQueueSize; + } +} diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java index a939840d62..27c81a1ee1 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/QueuedNotificationManager.java @@ -8,7 +8,9 @@ package org.opendaylight.yangtools.util.concurrent; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -182,6 +184,35 @@ public class QueuedNotificationManager implements NotificationManager } } + /** + * Returns {@link ListenerNotificationQueueStats} instances for each current listener + * notification task in progress. + */ + public List getListenerNotificationQueueStats() { + List statsList = new ArrayList<>( listenerCache.size() ); + for( NotificationTask task: listenerCache.values() ) { + statsList.add( new ListenerNotificationQueueStats( + task.listenerKey.getListener().getClass().getName(), + task.notificationQueue.size() ) ); + } + + return statsList ; + } + + /** + * Returns the maximum listener queue capacity. + */ + public int getMaxQueueCapacity(){ + return maxQueueCapacity; + } + + /** + * Returns the {@link Executor} to used for notification tasks. + */ + public Executor getExecutor(){ + return executor; + } + /** * Used as the listenerCache map key. We key by listener reference identity hashCode/equals. * Since we don't know anything about the listener class implementations and we're mixing diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java index 0548d7a091..110ac1eeda 100644 --- a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/SpecialExecutors.java @@ -9,7 +9,6 @@ package org.opendaylight.yangtools.util.concurrent; import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -74,7 +73,7 @@ public final class SpecialExecutors { FastThreadPoolExecutor executor = new FastThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix ); - executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); + executor.setRejectedExecutionHandler( CountingRejectedExecutionHandler.newCallerRunsPolicy() ); return executor; } @@ -130,7 +129,7 @@ public final class SpecialExecutors { CachedThreadPoolExecutor executor = new CachedThreadPoolExecutor( maximumPoolSize, maximumQueueSize, threadPrefix ); - executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy() ); + executor.setRejectedExecutionHandler( CountingRejectedExecutionHandler.newCallerRunsPolicy() ); return executor; } diff --git a/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java new file mode 100644 index 0000000000..38b5d9017f --- /dev/null +++ b/common/util/src/main/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueue.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util.concurrent; + +import java.util.Collection; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicLongFieldUpdater; + +/** + * A {@link LinkedBlockingQueue} that tracks the largest queue size for debugging. + * + * @author Thomas Pantelis + * + * @param the element t.ype + */ +public class TrackingLinkedBlockingQueue extends LinkedBlockingQueue { + + private static final long serialVersionUID = 1L; + + /** + * Holds largestQueueSize, this long field should be only accessed + * using {@value #LARGEST_QUEUE_SIZE_UPDATER} + */ + private volatile long largestQueueSize = 0; + + @SuppressWarnings("rawtypes") + private static AtomicLongFieldUpdater LARGEST_QUEUE_SIZE_UPDATER = AtomicLongFieldUpdater.newUpdater(TrackingLinkedBlockingQueue.class, "largestQueueSize"); + + /** + * @see LinkedBlockingQueue#LinkedBlockingQueue + */ + public TrackingLinkedBlockingQueue() { + super(); + } + + /** + * @see LinkedBlockingQueue#LinkedBlockingQueue(Collection) + */ + public TrackingLinkedBlockingQueue( Collection c ) { + super(c); + } + + /** + * @see LinkedBlockingQueue#LinkedBlockingQueue(int) + */ + public TrackingLinkedBlockingQueue( int capacity ) { + super(capacity); + } + + /** + * Returns the largest queue size. + */ + public long getLargestQueueSize(){ + return largestQueueSize; + } + + @Override + public boolean offer( E e, long timeout, TimeUnit unit ) throws InterruptedException { + if( super.offer( e, timeout, unit ) ) { + updateLargestQueueSize(); + return true; + } + + return false; + } + + @Override + public boolean offer( E e ) { + if( super.offer( e ) ) { + updateLargestQueueSize(); + return true; + } + + return false; + } + + @Override + public void put( E e ) throws InterruptedException { + super.put( e ); + updateLargestQueueSize(); + } + + @Override + public boolean add( E e ) { + boolean result = super.add( e ); + updateLargestQueueSize(); + return result; + } + + @Override + public boolean addAll( Collection c ) { + try { + return super.addAll( c ); + } finally { + updateLargestQueueSize(); + } + } + + private void updateLargestQueueSize() { + long size = size(); + long largest = largestQueueSize; + if( size > largest ) { + LARGEST_QUEUE_SIZE_UPDATER.compareAndSet(this, largest, size ); + } + } +} diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatsTrackerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatsTrackerTest.java new file mode 100644 index 0000000000..70d63df2dc --- /dev/null +++ b/common/util/src/test/java/org/opendaylight/yangtools/util/DurationStatsTrackerTest.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Unit tests for DurationStatsTracker. + * + * @author Thomas Pantelis + */ +public class DurationStatsTrackerTest { + + @Test + public void test() { + + DurationStatsTracker tracker = new DurationStatsTracker(); + + tracker.addDuration(10000); + assertEquals("getTotalDurations", 1, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 10000.0, tracker.getAverageDuration(), 0.1); + assertEquals("getLongestDuration", 10000, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 10000, tracker.getShortestDuration()); + + tracker.addDuration(30000); + assertEquals("getTotalDurations", 2, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 20000.0, tracker.getAverageDuration(), 0.1); + assertEquals("getLongestDuration", 30000, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 10000, tracker.getShortestDuration()); + + verifyDisplayableString("getDisplayableAverageDuration", + tracker.getDisplayableAverageDuration(), "20.0"); + verifyDisplayableString("getDisplayableLongestDuration", + tracker.getDisplayableLongestDuration(), "30.0"); + verifyDisplayableString("getDisplayableShortestDuration", + tracker.getDisplayableShortestDuration(), "10.0"); + + tracker.addDuration(10000); + assertEquals("getTotalDurations", 3, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 16666.0, tracker.getAverageDuration(), 1.0); + assertEquals("getLongestDuration", 30000, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 10000, tracker.getShortestDuration()); + + tracker.addDuration(5000); + assertEquals("getTotalDurations", 4, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 13750.0, tracker.getAverageDuration(), 1.0); + assertEquals("getLongestDuration", 30000, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 5000, tracker.getShortestDuration()); + + tracker.reset(); + assertEquals("getTotalDurations", 0, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 0.0, tracker.getAverageDuration(), 0.1); + assertEquals("getLongestDuration", 0, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 0, tracker.getShortestDuration()); + + tracker.addDuration(10000); + assertEquals("getTotalDurations", 1, tracker.getTotalDurations()); + assertEquals("getAverageDuration", 10000.0, tracker.getAverageDuration(), 0.1); + assertEquals("getLongestDuration", 10000, tracker.getLongestDuration()); + assertEquals("getShortestDuration", 10000, tracker.getShortestDuration()); + } + + private void verifyDisplayableString(String name, String actual, String expPrefix) { + assertEquals(name + " starts with " + expPrefix + ". Actual: " + actual, + true, actual.startsWith(expPrefix)); + } +} diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandlerTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandlerTest.java new file mode 100644 index 0000000000..42c939ab34 --- /dev/null +++ b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/CountingRejectedExecutionHandlerTest.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.junit.After; +import org.junit.Test; +import org.opendaylight.yangtools.util.ExecutorServiceUtil; +import org.opendaylight.yangtools.util.concurrent.ThreadPoolExecutorTest.Task; + +/** + * Unit tests for CountingRejectedExecutionHandler. + * + * @author Thomas Pantelis + */ +public class CountingRejectedExecutionHandlerTest { + + private ThreadPoolExecutor executor; + + @After + public void tearDown() { + if( executor != null ) { + executor.shutdownNow(); + } + } + + @Test + public void testCallerRunsPolicyHandler() throws InterruptedException { + + int nTasks = 5; + CountDownLatch tasksRunLatch = new CountDownLatch( 1 ); + CountDownLatch blockLatch = new CountDownLatch( 1 ); + + executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, + ExecutorServiceUtil.offerFailingBlockingQueue( new LinkedBlockingQueue() ) ); + + CountingRejectedExecutionHandler countingHandler = + CountingRejectedExecutionHandler.newCallerRunsPolicy(); + executor.setRejectedExecutionHandler( countingHandler ); + + executor.execute( new Task( tasksRunLatch, blockLatch ) ); + + for( int i = 0; i < nTasks - 1; i++ ) { + executor.execute( new Task( null, null, null, null, 0 ) ); + } + + assertEquals( "getRejectedTaskCount", nTasks - 1, countingHandler.getRejectedTaskCount() ); + + blockLatch.countDown(); + + assertEquals( "Tasks complete", true, tasksRunLatch.await( 5, TimeUnit.SECONDS ) ); + } + + @Test + public void testAbortPolicyHandler() throws InterruptedException { + + int nTasks = 5; + CountDownLatch tasksRunLatch = new CountDownLatch( 1 ); + CountDownLatch blockLatch = new CountDownLatch( 1 ); + + executor = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.SECONDS, + ExecutorServiceUtil.offerFailingBlockingQueue( new LinkedBlockingQueue() ) ); + + CountingRejectedExecutionHandler countingHandler = + CountingRejectedExecutionHandler.newAbortPolicy(); + executor.setRejectedExecutionHandler( countingHandler ); + + executor.execute( new Task( tasksRunLatch, blockLatch ) ); + + for( int i = 0; i < nTasks - 1; i++ ) { + try { + executor.execute( new Task( null, null, null, null, 0 ) ); + fail( "Expected RejectedExecutionException" ); + } catch( RejectedExecutionException e ) { + // Expected + } + } + + assertEquals( "getRejectedTaskCount", nTasks - 1, countingHandler.getRejectedTaskCount() ); + + blockLatch.countDown(); + + assertEquals( "Tasks complete", true, tasksRunLatch.await( 5, TimeUnit.SECONDS ) ); + } +} diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java index 8270e45d35..4d280536a1 100644 --- a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java +++ b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/ThreadPoolExecutorTest.java @@ -148,8 +148,9 @@ public class ThreadPoolExecutorTest { System.out.println(); } - private static class Task implements Runnable { + static class Task implements Runnable { final CountDownLatch tasksRunLatch; + final CountDownLatch blockLatch; final ConcurrentMap taskCountPerThread; final AtomicReference threadError; final String expThreadPrefix; @@ -162,16 +163,28 @@ public class ThreadPoolExecutorTest { this.threadError = threadError; this.expThreadPrefix = expThreadPrefix; this.delay = delay; + blockLatch = null; + } + + Task( CountDownLatch tasksRunLatch, CountDownLatch blockLatch ) { + this.tasksRunLatch = tasksRunLatch; + this.blockLatch = blockLatch; + this.taskCountPerThread = null; + this.threadError = null; + this.expThreadPrefix = null; + this.delay = 0; } @Override public void run() { try { - if( delay > 0 ) { - try { + try { + if( delay > 0 ) { TimeUnit.MICROSECONDS.sleep( delay ); - } catch( InterruptedException e ) {} - } + } else if( blockLatch != null ) { + blockLatch.await(); + } + } catch( InterruptedException e ) {} if( expThreadPrefix != null ) { assertEquals( "Thread name starts with " + expThreadPrefix, true, diff --git a/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java new file mode 100644 index 0000000000..e1119c3716 --- /dev/null +++ b/common/util/src/test/java/org/opendaylight/yangtools/util/concurrent/TrackingLinkedBlockingQueueTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2014 Brocade Communications 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.util.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; + +/** + * Unit tests for TrackingLinkedBlockingQueue. + * + * @author Thomas Pantelis + */ +public class TrackingLinkedBlockingQueueTest { + + @Test + public void testOffer() throws InterruptedException { + + TrackingLinkedBlockingQueue queue = new TrackingLinkedBlockingQueue<>( 2 ); + + assertEquals( "offer", true, queue.offer( "1" ) ); + assertEquals( "getLargestQueueSize", 1, queue.getLargestQueueSize() ); + assertEquals( "size", 1, queue.size() ); + + assertEquals( "offer", true, queue.offer( "2", 1, TimeUnit.MILLISECONDS ) ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + + assertEquals( "offer", false, queue.offer( "3" ) ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + + assertEquals( "offer", false, queue.offer( "4", 1, TimeUnit.MILLISECONDS ) ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + } + + @Test + public void testPut() throws InterruptedException { + + TrackingLinkedBlockingQueue queue = new TrackingLinkedBlockingQueue<>(); + + queue.put( "1" ); + assertEquals( "getLargestQueueSize", 1, queue.getLargestQueueSize() ); + assertEquals( "size", 1, queue.size() ); + + queue.put( "2" ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + } + + @Test + public void testAdd() { + + TrackingLinkedBlockingQueue queue = new TrackingLinkedBlockingQueue<>( 2 ); + + assertEquals( "add", true, queue.add( "1" ) ); + assertEquals( "getLargestQueueSize", 1, queue.getLargestQueueSize() ); + assertEquals( "size", 1, queue.size() ); + + assertEquals( "add", true, queue.add( "2" ) ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + + try { + queue.add( "3" ); + fail( "Expected IllegalStateException" ); + } catch( IllegalStateException e ) { + // Expected + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + } + } + + @Test + public void testAddAll() { + + TrackingLinkedBlockingQueue queue = new TrackingLinkedBlockingQueue<>( 3 ); + + queue.addAll( Arrays.asList( "1", "2" ) ); + assertEquals( "getLargestQueueSize", 2, queue.getLargestQueueSize() ); + assertEquals( "size", 2, queue.size() ); + + try { + queue.addAll( Arrays.asList( "3", "4" ) ); + fail( "Expected IllegalStateException" ); + } catch( IllegalStateException e ) { + // Expected + assertEquals( "getLargestQueueSize", 3, queue.getLargestQueueSize() ); + assertEquals( "size", 3, queue.size() ); + } + } +} diff --git a/pom.xml b/pom.xml index 90a3e97e4d..7ad393fbc3 100644 --- a/pom.xml +++ b/pom.xml @@ -31,7 +31,8 @@ restconf websocket yang - + benchmarks + diff --git a/restconf/restconf-client-impl/src/main/java/org/opendaylight/yangtools/restconf/client/RestListenableEventStreamContext.java b/restconf/restconf-client-impl/src/main/java/org/opendaylight/yangtools/restconf/client/RestListenableEventStreamContext.java index ce0ca2cc88..a14ea33b5f 100644 --- a/restconf/restconf-client-impl/src/main/java/org/opendaylight/yangtools/restconf/client/RestListenableEventStreamContext.java +++ b/restconf/restconf-client-impl/src/main/java/org/opendaylight/yangtools/restconf/client/RestListenableEventStreamContext.java @@ -160,10 +160,8 @@ public class RestListenableEventStreamContext im } try { this.listenerCallbackMethod.invoke(message); - } catch (IllegalAccessException e) { - throw new IllegalStateException(e.getMessage()); - } catch (InvocationTargetException e) { - throw new IllegalStateException(e.getMessage()); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new IllegalStateException("Failed to invoke callback", e); } } diff --git a/restconf/restconf-jaxrs-api/src/main/java/org/opendaylight/yangtools/RestRestconfService.java b/restconf/restconf-jaxrs-api/src/main/java/org/opendaylight/yangtools/RestRestconfService.java index 11083c3129..a3c7146046 100644 --- a/restconf/restconf-jaxrs-api/src/main/java/org/opendaylight/yangtools/RestRestconfService.java +++ b/restconf/restconf-jaxrs-api/src/main/java/org/opendaylight/yangtools/RestRestconfService.java @@ -25,6 +25,8 @@ import org.opendaylight.yangtools.yang.data.api.CompositeNode; @Path("restconf") public interface RestRestconfService { + public static final String IDENTIFIER = "identifier"; + public static final String INPUT = "input"; public static final String XML = "+xml"; public static final String JSON = "+json"; @@ -45,48 +47,48 @@ public interface RestRestconfService { @Consumes({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public String invokeRpc(@PathParam("identifier") String identifier, @QueryParam("input") CompositeNode payload); + public String invokeRpc(@PathParam(IDENTIFIER) String identifier, @QueryParam(INPUT) CompositeNode payload); @POST @Path("/operations/{identifier}") @Produces({Draft01.MediaTypes.DATA+JSON,Draft01.MediaTypes.DATA+XML, Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public String invokeRpc(@PathParam("identifier") String identifier); + public String invokeRpc(@PathParam(IDENTIFIER) String identifier); @GET @Path("/config/{identifier:.+}") @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public String readConfigurationData(@PathParam("identifier") String identifier); + public String readConfigurationData(@PathParam(IDENTIFIER) String identifier); @GET @Path("/operational/{identifier:.+}") @Produces({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public String readOperationalData(@PathParam("identifier") String identifier); + public String readOperationalData(@PathParam(IDENTIFIER) String identifier); @PUT @Path("/config/{identifier:.+}") @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public Response updateConfigurationData(@PathParam("identifier") String identifier,@QueryParam("input") CompositeNode payload); + public Response updateConfigurationData(@PathParam(IDENTIFIER) String identifier,@QueryParam(INPUT) CompositeNode payload); @POST @Path("/config/{identifier:.+}") @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public Response createConfigurationData(@PathParam("identifier") String identifier, @QueryParam("input") CompositeNode payload); + public Response createConfigurationData(@PathParam(IDENTIFIER) String identifier, @QueryParam(INPUT) CompositeNode payload); @POST @Path("/config") @Consumes({Draft02.MediaTypes.DATA+JSON,Draft02.MediaTypes.DATA+XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML, MediaType.TEXT_XML}) - public Response createConfigurationData( @QueryParam("input") CompositeNode payload); + public Response createConfigurationData( @QueryParam(INPUT) CompositeNode payload); @DELETE @Path("/config/{identifier:.+}") - public Response deleteConfigurationData(@PathParam("identifier") String identifier); + public Response deleteConfigurationData(@PathParam(IDENTIFIER) String identifier); } diff --git a/restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/XmlTools.java b/restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/XmlTools.java index 05b95be0e3..9ca3a2345b 100644 --- a/restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/XmlTools.java +++ b/restconf/restconf-util/src/main/java/org/opendaylight/yangtools/restconf/utils/XmlTools.java @@ -152,8 +152,8 @@ public final class XmlTools { restModule.setName(eElement.getElementsByTagName("name").item(0).getTextContent()); restModule.setNamespace(eElement.getElementsByTagName("namespace").item(0).getTextContent()); restModule.setRevision(eElement.getElementsByTagName("revision").item(0).getTextContent()); - } catch (NullPointerException npe){ - throw new IllegalStateException("Incomplete module data in xml."); + } catch (NullPointerException e) { + throw new IllegalStateException("Incomplete module data in xml.", e); } return restModule; } diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java index 2966fb982f..d628de016c 100644 --- a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/BindingMapping.java @@ -12,10 +12,12 @@ import static com.google.common.base.Preconditions.checkArgument; import com.google.common.base.CharMatcher; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableSet; + import java.text.SimpleDateFormat; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; + import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.common.QNameModule; @@ -66,11 +68,11 @@ public final class BindingMapping { throw new UnsupportedOperationException("Utility class should not be instantiated"); } - public static final String getRootPackageName(final QName module) { + public static String getRootPackageName(final QName module) { return getRootPackageName(module.getModule()); } - public static final String getRootPackageName(final QNameModule module) { + public static String getRootPackageName(final QNameModule module) { checkArgument(module != null, "Module must not be null"); checkArgument(module.getRevision() != null, "Revision must not be null"); checkArgument(module.getNamespace() != null, "Namespace must not be null"); @@ -132,22 +134,22 @@ public final class BindingMapping { return builder.toString(); } - public static final String getMethodName(final QName name) { + public static String getMethodName(final QName name) { checkArgument(name != null, "Name should not be null."); return getMethodName(name.getLocalName()); } - public static final String getClassName(final String localName) { + public static String getClassName(final String localName) { checkArgument(localName != null, "Name should not be null."); return toFirstUpper(toCamelCase(localName)); } - public static final String getMethodName(final String yangIdentifier) { + public static String getMethodName(final String yangIdentifier) { checkArgument(yangIdentifier != null,"Identifier should not be null"); return toFirstLower(toCamelCase(yangIdentifier)); } - public static final String getClassName(final QName name) { + public static String getClassName(final QName name) { checkArgument(name != null, "Name should not be null."); return toFirstUpper(toCamelCase(name.getLocalName())); } @@ -160,7 +162,7 @@ public final class BindingMapping { return potential; } - private static final String toCamelCase(final String rawString) { + private static String toCamelCase(final String rawString) { checkArgument(rawString != null, "String should not be null"); Iterable components = CAMEL_SPLITTER.split(rawString); StringBuilder builder = new StringBuilder(); @@ -170,7 +172,7 @@ public final class BindingMapping { return checkNumericPrefix(builder.toString()); } - private static final String checkNumericPrefix(final String rawString) { + private static String checkNumericPrefix(final String rawString) { if (rawString == null || rawString.isEmpty()) { return rawString; } diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java index f9f4c5c7b5..bc7474a9f2 100644 --- a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java +++ b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/util/BindingReflections.java @@ -16,12 +16,15 @@ import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet.Builder; +import com.google.common.collect.Sets; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Type; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -581,4 +584,51 @@ public class BindingReflections { public static Map>, Augmentation> getAugmentations(final Augmentable input) { return AugmentationFieldGetter.getGetter(input.getClass()).getAugmentations(input); } + + /** + * + * Determines if two augmentation classes or case classes represents same data. + *

+ * Two augmentations or cases could be substituted only if and if: + *

    + *
  • Both implements same interfaces
  • + *
  • Both have same children
  • + *
  • If augmentations: Both have same augmentation target class. Target class was generated for data node in grouping.
  • + *
  • If cases: Both are from same choice. Choice class was generated for data node in grouping.
  • + *
+ *

+ * Explanation: Binding Specification reuses classes generated for groupings as part of normal data tree, + * this classes from grouping could be used at various locations and user may not be aware of it + * and may use incorrect case or augmentation in particular subtree (via copy constructors, etc). + * + * @param potential Class which is potential substition + * @param target Class which should be used at particular subtree + * @return true if and only if classes represents same data. + */ + @SuppressWarnings({"rawtypes","unchecked"}) + public static boolean isSubstitutionFor(Class potential, Class target) { + HashSet subImplemented = Sets.newHashSet(potential.getInterfaces()); + HashSet targetImplemented = Sets.newHashSet(target.getInterfaces()); + if(!subImplemented.equals(targetImplemented)) { + return false; + } + if(Augmentation.class.isAssignableFrom(potential) + && !BindingReflections.findAugmentationTarget(potential).equals(BindingReflections.findAugmentationTarget(target))) { + return false; + } + for(Method potentialMethod : potential.getMethods()) { + try { + Method targetMethod = target.getMethod(potentialMethod.getName(), potentialMethod.getParameterTypes()); + if(!potentialMethod.getReturnType().equals(targetMethod.getReturnType())) { + return false; + } + } catch (NoSuchMethodException e) { + // Counterpart method is missing, so classes could not be substituted. + return false; + } catch (SecurityException e) { + throw new IllegalStateException("Could not compare methods",e); + } + } + return true; + } } diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NodeModificationBuilder.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NodeModificationBuilder.java index b8b6d14d1b..fc86f71e13 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NodeModificationBuilder.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/NodeModificationBuilder.java @@ -16,26 +16,26 @@ package org.opendaylight.yangtools.yang.data.api; @Deprecated public interface NodeModificationBuilder { - abstract Node getMutableEquivalent(Node originalNode); + Node getMutableEquivalent(Node originalNode); - abstract CompositeNode buildDiffTree(); + CompositeNode buildDiffTree(); - abstract void mergeNode(MutableCompositeNode alteredNode); + void mergeNode(MutableCompositeNode alteredNode); - abstract void removeNode(MutableCompositeNode deadNode); + void removeNode(MutableCompositeNode deadNode); - abstract void removeNode(MutableSimpleNode deadNode); + void removeNode(MutableSimpleNode deadNode); - abstract void deleteNode(MutableSimpleNode deadNode); + void deleteNode(MutableSimpleNode deadNode); - abstract void deleteNode(MutableCompositeNode deadNode); + void deleteNode(MutableCompositeNode deadNode); - abstract void replaceNode(MutableCompositeNode replacementNode); + void replaceNode(MutableCompositeNode replacementNode); - abstract void replaceNode(MutableSimpleNode replacementNode); + void replaceNode(MutableSimpleNode replacementNode); - abstract void addNode(MutableCompositeNode newNode); + void addNode(MutableCompositeNode newNode); - abstract void addNode(MutableSimpleNode newNode); + void addNode(MutableSimpleNode newNode); } diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java index dba2e726fa..e9f940a455 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/YangInstanceIdentifier.java @@ -31,6 +31,7 @@ import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.concepts.Path; import org.opendaylight.yangtools.util.HashCodeBuilder; import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode; /** @@ -311,7 +312,7 @@ public final class YangInstanceIdentifier implements Path * This interface itself is used as common parent for actual @@ -339,6 +340,17 @@ public final class YangInstanceIdentifier implements Path indent = new ArrayDeque<>(); + private final String indentStr; + + public LoggingNormalizedNodeStreamWriter() { + this(DEFAULT_INDENT_SIZE); + } + + public LoggingNormalizedNodeStreamWriter(final int indentSize) { + this.indentStr = Strings.repeat(" ", indentSize); + indent.push(""); + } private String ind() { - return Strings.repeat(" ", currentIndent); + return indent.peek(); } private void decIndent() { - Preconditions.checkState(currentIndent >= 0, "Unexpected indentation %s", currentIndent); - currentIndent -= indentSize; + indent.pop(); } private void incIndent() { - currentIndent += indentSize; + indent.push(ind() + indentStr); } @Override - public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IllegalStateException { + public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}[](no key)", ind(), name); incIndent(); } @Override - public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}(no key)", ind(), name); incIndent(); } @Override - public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) { } @Override - public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startMapNode(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}(key)", ind(), name); incIndent(); } @Override - public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) - throws IllegalArgumentException { + public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) { LOG.debug("{}{}[](key)", ind(), identifier); incIndent(); } @Override - public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startLeafSet(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}(leaf-list)", ind(), name); incIndent(); } @Override - public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startContainerNode(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}(container)", ind(), name); incIndent(); } @Override - public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) { LOG.debug("{}{}(choice)", ind(), name); incIndent(); } @Override - public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException { + public void startAugmentationNode(final AugmentationIdentifier identifier) { LOG.debug("{}{}(augmentation)", ind(), identifier); incIndent(); } @Override - public void leafSetEntryNode(final Object value) throws IllegalArgumentException { + public void leafSetEntryNode(final Object value) { LOG.debug("{}{}({}) ", ind(), value, value.getClass().getSimpleName()); } @Override - public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException { + public void leafNode(final NodeIdentifier name, final Object value) { LOG.debug("{}{}(leaf({}))=", ind(), name, value.getClass().getSimpleName(), value); } @@ -103,17 +114,17 @@ public class LoggingNormalizedNodeStreamWriter implements NormalizedNodeStreamWr } @Override - public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException { + public void anyxmlNode(final NodeIdentifier name, final Object value) { LOG.debug("{}{}(anyxml)=", ind(), name, value); } @Override - public void flush() throws IOException { + public void flush() { LOG.trace("<>"); } @Override - public void close() throws IOException { + public void close() { LOG.debug("<>"); } } \ No newline at end of file diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java index a0068c303a..b0c05243ea 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java @@ -16,6 +16,8 @@ import java.io.Closeable; import java.io.Flushable; import java.io.IOException; +import javax.xml.stream.XMLStreamReader; + import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; @@ -31,7 +33,10 @@ import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; /** - * This is an experimental + * This is an experimental iterator over a {@link NormalizedNode}. This is essentially + * the opposite of a {@link XMLStreamReader} -- unlike instantiating an iterator over + * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows + * us to write multiple nodes. */ @Beta public final class NormalizedNodeWriter implements Closeable, Flushable { @@ -41,10 +46,24 @@ public final class NormalizedNodeWriter implements Closeable, Flushable { this.writer = Preconditions.checkNotNull(writer); } + /** + * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. + * + * @param writer Backend writer + * @return A new instance. + */ public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) { return new NormalizedNodeWriter(writer); } + /** + * Iterate over the provided {@link NormalizedNode} and emit write + * events to the encapsulated {@link NormalizedNodeStreamWriter}. + * + * @param node Node + * @return + * @throws IOException when thrown from the backing writer. + */ public NormalizedNodeWriter write(final NormalizedNode node) throws IOException { if (wasProcessedAsCompositeNode(node)) { return this; diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ContainerNode.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ContainerNode.java index 6b9e36eb7a..588b884d8a 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ContainerNode.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/ContainerNode.java @@ -41,7 +41,18 @@ final class ContainerNode extends AbstractTreeNode { @Override public Optional getChild(final PathArgument key) { - return Optional.fromNullable(children.get(key)); + Optional explicitNode = Optional.fromNullable(children.get(key)); + if (explicitNode.isPresent()) { + return explicitNode; + } + final NormalizedNodeContainer> castedData = (NormalizedNodeContainer>) getData(); + Optional> value = castedData.getChild(key); + if (value.isPresent()) { + //FIXME: consider caching created Tree Nodes. + //We are safe to not to cache them, since written Tree Nodes are in read only snapshot. + return Optional.of(TreeNodeFactory.createTreeNode(value.get(), getVersion())); + } + return Optional.absent(); } @Override @@ -60,6 +71,26 @@ final class ContainerNode extends AbstractTreeNode { this.children = MapAdaptor.getDefaultInstance().takeSnapshot(parent.children); this.subtreeVersion = parent.getSubtreeVersion(); this.version = parent.getVersion(); + materializeChildVersion(); + } + + /** + * Traverse whole data tree and instantiate children for each data node. Set version of each MutableTreeNode + * accordingly to version in data node. + * + * Use this method if TreeNode is lazy initialized. + */ + private void materializeChildVersion() { + Preconditions.checkState(data instanceof NormalizedNodeContainer); + NormalizedNodeContainer> castedData = (NormalizedNodeContainer>) data; + + for(NormalizedNode childData : castedData.getValue()) { + PathArgument id = childData.getIdentifier(); + + if (!children.containsKey(id)) { + children.put(id, TreeNodeFactory.createTreeNode(childData, version)); + } + } } @Override @@ -97,22 +128,99 @@ final class ContainerNode extends AbstractTreeNode { } } - private static ContainerNode create(final Version version, final NormalizedNode data, - final Iterable> children) { + /** + * Method creates and returns Container root Node and whole subtree for each child node specified in children nodes. + *
+ * Reason why is method used recursively is that for each child in children nodes there is call to + * {@link TreeNodeFactory#createTreeNodeRecursively}. Each call to createTreeNodeRecursively + * calls either {@link #createNormalizedNodeRecursively} or {@link #createOrderedNodeRecursively} + * which depends on type of child node. + *
The root node that is returned holds reference to data node and whole subtree of children also containing references + * to data nodes. + * + * @param version version of indexed data + * @param data reference to data node + * @param children direct children of root node that is being created + * @return Root node with reference to data node and whole subtree of child nodes + */ + private static ContainerNode createNodeRecursively(final Version version, final NormalizedNode data, + final Iterable> children) { final Map map = new HashMap<>(); for (NormalizedNode child : children) { - map.put(child.getIdentifier(), TreeNodeFactory.createTreeNode(child, version)); + map.put(child.getIdentifier(), TreeNodeFactory.createTreeNodeRecursively(child, version)); } return new ContainerNode(data, version, map, version); } - public static ContainerNode create(final Version version, final NormalizedNodeContainer> container) { - return create(version, container, container.getValue()); + /** + * Method creates and returns Normalized Node Container as root and recursively creates whole subtree + * from all of the container child iterables stored in {@link org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer#getValue()} + *
+ * The reason why is this method called recursively is that in background method calls {@link TreeNodeFactory#createTreeNodeRecursively} + * for each child stored in NormalizedNode and after each child is created the method calls again {@link #createNormalizedNodeRecursively} method + * until all of the children are resolved. + * + * @param version version of indexed data + * @param container Normalized Node Container + * @return Normalized Node Container as root and all whole subtree created from container iterables. + */ + public static ContainerNode createNormalizedNodeRecursively(final Version version, + final NormalizedNodeContainer> container) { + return createNodeRecursively(version, container, container.getValue()); + } + + /** + * Method creates and returns Ordered Node Container as root and recursively creates whole subtree + * from all of the container child iterables stored in {@link org.opendaylight.yangtools.yang.data.api.schema.OrderedNodeContainer#getValue()} + *
+ * The reason why is this method called recursively is that in background method calls {@link TreeNodeFactory#createTreeNodeRecursively} + * for each child stored in NormalizedNode and after each child is created the method calls again {@link #createNormalizedNodeRecursively} method + * until all of the children are resolved. + * + * @param version version of indexed data + * @param container Ordered Node Container + * @return Normalized Ordered Container as root and all whole subtree created from container iterables. + */ + public static ContainerNode createOrderedNodeRecursively(final Version version, + final OrderedNodeContainer> container) { + return createNodeRecursively(version, container, container.getValue()); } - public static ContainerNode create(final Version version, final OrderedNodeContainer> container) { - return create(version, container, container.getValue()); + /** + * Creates and returns single instance of Normalized Node Container with provided version and data reference stored in NormalizedNodeContainer. + * + * @param version version of indexed data + * @param container Normalized Node Container + * @return single instance of Normalized node with provided version and data reference stored in NormalizedNodeContainer + */ + public static ContainerNode createNormalizedNode(final Version version, + final NormalizedNodeContainer> container) { + return createNode(version, container); + } + + /** + * Creates and returns single instance of Ordered Node Container with provided version and data reference stored in OrderedNodeContainer. + * + * @param version version of indexed data + * @param container Ordered Node Container + * @return single instance of Ordered Node Container with provided version and data reference stored in OrderedNodeContainer. + */ + public static ContainerNode createOrderedNode(final Version version, + final OrderedNodeContainer> container) { + return createNode(version, container); + } + + /** + * Creates and returns single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode. + * + * @param version version of indexed data + * @param data NormalizedNode data container + * @return single instance of {@link ContainerNode} with provided version and data reference stored in NormalizedNode. + */ + private static ContainerNode createNode(final Version version, final NormalizedNode data) { + final Map map = new HashMap<>(); + return new ContainerNode(data, version, map, version); } } diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java index 656814162f..4f0a2201d7 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/tree/spi/TreeNodeFactory.java @@ -28,19 +28,40 @@ public final class TreeNodeFactory { * @param version data node version * @return new AbstractTreeNode instance, covering the data tree provided */ - public static final TreeNode createTreeNode(final NormalizedNode data, final Version version) { + public static final TreeNode createTreeNodeRecursively(final NormalizedNode data, final Version version) { if (data instanceof NormalizedNodeContainer) { @SuppressWarnings("unchecked") NormalizedNodeContainer> container = (NormalizedNodeContainer>) data; - return ContainerNode.create(version, container); + return ContainerNode.createNormalizedNodeRecursively(version, container); } if (data instanceof OrderedNodeContainer) { @SuppressWarnings("unchecked") OrderedNodeContainer> container = (OrderedNodeContainer>) data; - return ContainerNode.create(version, container); + return ContainerNode.createOrderedNodeRecursively(version, container); } return new ValueNode(data, version); } + + /** + * Create a new AbstractTreeNode from a data node. + * + * @param data data node + * @param version data node version + * @return new AbstractTreeNode instance, covering the data tree provided + */ + public static final TreeNode createTreeNode(final NormalizedNode data, final Version version) { + if (data instanceof NormalizedNodeContainer) { + @SuppressWarnings("unchecked") + NormalizedNodeContainer> container = (NormalizedNodeContainer>) data; + return ContainerNode.createNormalizedNode(version, container); + } + if (data instanceof OrderedNodeContainer) { + @SuppressWarnings("unchecked") + OrderedNodeContainer> container = (OrderedNodeContainer>) data; + return ContainerNode.createOrderedNode(version, container); + } + return new ValueNode(data, version); + } } diff --git a/yang/yang-data-codec-gson/pom.xml b/yang/yang-data-codec-gson/pom.xml index c8e1719906..6947a81211 100644 --- a/yang/yang-data-codec-gson/pom.xml +++ b/yang/yang-data-codec-gson/pom.xml @@ -33,6 +33,10 @@ org.opendaylight.yangtools yang-data-impl + + org.opendaylight.yangtools + yang-data-util + org.opendaylight.yangtools yang-parser-impl diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java index 3284df2cdd..fd0d950722 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AbstractNodeDataWithSchema.java @@ -7,27 +7,43 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +/** + * Utility abstract class for tracking parser state, as needed by StAX-like parser. + */ +@Beta abstract class AbstractNodeDataWithSchema { - private final DataSchemaNode schema; - public AbstractNodeDataWithSchema(final DataSchemaNode schema) { - this.schema = schema; + protected AbstractNodeDataWithSchema(final DataSchemaNode schema) { + this.schema = Preconditions.checkNotNull(schema); } + /** + * Return the associated schema node. + * @return + */ public final DataSchemaNode getSchema() { return schema; } - protected abstract void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException; + /** + * Emit this node's events into the specified writer. + * + * @param writer Target writer + * @throws IOException reported when thrown by the writer. + */ + public abstract void write(final NormalizedNodeStreamWriter writer) throws IOException; - protected NodeIdentifier provideNodeIdentifier() { + protected final NodeIdentifier provideNodeIdentifier() { return new NodeIdentifier(schema.getQName()); } @@ -50,16 +66,8 @@ abstract class AbstractNodeDataWithSchema { if (getClass() != obj.getClass()) { return false; } - AbstractNodeDataWithSchema other = (AbstractNodeDataWithSchema) obj; - if (schema == null) { - if (other.schema != null) { - return false; - } - } else if (!schema.equals(other.schema)) { - return false; - } - - return true; + final AbstractNodeDataWithSchema other = (AbstractNodeDataWithSchema) obj; + return schema.equals(other.schema); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java index aaef749ffa..2c674d6e46 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/AnyXmlNodeDataWithSchema.java @@ -19,8 +19,8 @@ class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { -// FIXME: should be changed according to format of value - nnStreamWriter.anyxmlNode(provideNodeIdentifier(), getValue()); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + // FIXME: should be changed according to format of value + writer.anyxmlNode(provideNodeIdentifier(), getValue()); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java index e3fdaede26..67e3c59389 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CaseNodeDataWithSchema.java @@ -6,10 +6,6 @@ * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.data.codec.gson; - -import java.io.IOException; - -import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema { @@ -18,8 +14,4 @@ class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema { super(schema); } - @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - super.writeToStream(nnStreamWriter); - } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java index 39c1589053..6f840bb3d6 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ChoiceNodeDataWithSchema.java @@ -7,8 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; - import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; @@ -30,7 +28,7 @@ class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - public CompositeNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) { + protected CompositeNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) { CaseNodeDataWithSchema newChild = new CaseNodeDataWithSchema((ChoiceCaseNode) schema); caseNodeDataWithSchema = newChild; addCompositeChild(newChild); @@ -42,10 +40,10 @@ class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - nnStreamWriter.startChoiceNode(provideNodeIdentifier(), UNKNOWN_SIZE); - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + writer.startChoiceNode(provideNodeIdentifier(), childSizeHint()); + super.write(writer); + writer.endNode(); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CodecFactory.java new file mode 100644 index 0000000000..dba5ed7d24 --- /dev/null +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CodecFactory.java @@ -0,0 +1,106 @@ +/* + * 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.yangtools.yang.data.codec.gson; + +import com.google.common.annotations.Beta; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; + +import org.opendaylight.yangtools.concepts.Codec; +import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec; +import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.IdentityrefType; +import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class is implementation-internal and subject to change. Please do not use it. + */ +@Beta +final class CodecFactory { + private static final Logger LOG = LoggerFactory.getLogger(CodecFactory.class); + private static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodec() { + @Override + public String serialize(final Object data) { + return String.valueOf(data); + } + + @Override + public Object deserialize(final String data) { + return data; + } + }; + private static final Codec NULL_CODEC = new Codec() { + @Override + public Object deserialize(final Object input) { + return null; + } + + @Override + public Object serialize(final Object input) { + return null; + } + }; + + + private static TypeDefinition resolveBaseTypeFrom(final TypeDefinition type) { + TypeDefinition superType = type; + while (superType.getBaseType() != null) { + superType = superType.getBaseType(); + } + return superType; + } + + private final LoadingCache, Codec> codecs = + CacheBuilder.newBuilder().softValues().build(new CacheLoader, Codec>() { + @Override + public Codec load(final TypeDefinition key) throws Exception { + final TypeDefinition type = resolveBaseTypeFrom(key); + + if (type instanceof InstanceIdentifierType) { + return iidCodec; + } + if (type instanceof IdentityrefType) { + return idrefCodec; + } + if (type instanceof LeafrefTypeDefinition) { + return LEAFREF_DEFAULT_CODEC; + } + + final TypeDefinitionAwareCodec> codec = TypeDefinitionAwareCodec.from(type); + if (codec == null) { + LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName().getLocalName()); + return NULL_CODEC; + } + + return codec; + } + }); + + private final Codec iidCodec; + private final Codec idrefCodec; + + private CodecFactory(final SchemaContext context) { + iidCodec = new JSONStringInstanceIdentifierCodec(context); + idrefCodec = new JSONStringIdentityrefCodec(context); + } + + public static CodecFactory create(final SchemaContext context) { + return new CodecFactory(context); + } + + @SuppressWarnings("unchecked") + public final Codec codecFor(final TypeDefinition typeDefinition) { + return (Codec) codecs.getUnchecked(typeDefinition); + } +} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java index 83d715cefe..c2997a84d7 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/CompositeNodeDataWithSchema.java @@ -7,17 +7,21 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.Collections2; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Multimap; + import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Map.Entry; -import java.util.Set; + +import javax.annotation.Nonnull; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; @@ -33,124 +37,117 @@ import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +/** + * A node which is composed of multiple simpler nodes. + */ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { + private static final Function QNAME_FUNCTION = new Function() { + @Override + public QName apply(@Nonnull final DataSchemaNode input) { + return input.getQName(); + } + }; /** * nodes which were added to schema via augmentation and are present in data input */ - protected Map> augmentationsToChild = new HashMap<>(); + private final Multimap augmentationsToChild = ArrayListMultimap.create(); /** - * remaining data nodes (which aren't added via augment). Every of them should have the same QName + * remaining data nodes (which aren't added via augment). Every of one them should have the same QName. */ - protected List childs = new ArrayList<>(); + private final List children = new ArrayList<>(); public CompositeNodeDataWithSchema(final DataSchemaNode schema) { super(schema); } - public AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) { - SimpleNodeDataWithSchema newChild = null; - if (schema instanceof LeafSchemaNode) { - newChild = new LeafNodeDataWithSchema(schema); - } else if (schema instanceof AnyXmlSchemaNode) { - newChild = new AnyXmlNodeDataWithSchema(schema); + public AbstractNodeDataWithSchema addChild(final Deque schemas) { + Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema"); + + // Pop the first node... + final DataSchemaNode schema = schemas.pop(); + if (schemas.isEmpty()) { + // Simple, direct node + return addChild(schema); } - if (newChild != null) { + // The choice/case mess, reuse what we already popped + final DataSchemaNode choiceCandidate = schema; + Preconditions.checkArgument(choiceCandidate instanceof ChoiceNode, + "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName()); + final ChoiceNode choiceNode = (ChoiceNode) choiceCandidate; - AugmentationSchema augSchema = null; - if (schema.isAugmenting()) { - augSchema = findCorrespondingAugment(getSchema(), schema); - } - if (augSchema != null) { - addChildToAugmentation(augSchema, newChild); - } else { - addChild(newChild); - } - return newChild; - } - return null; - } + final DataSchemaNode caseCandidate = schemas.pop(); + Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode, + "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName()); + final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate; - private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) { - List childsInAugment = augmentationsToChild.get(augSchema); - if (childsInAugment == null) { - childsInAugment = new ArrayList<>(); - augmentationsToChild.put(augSchema, childsInAugment); + AugmentationSchema augSchema = null; + if (choiceCandidate.isAugmenting()) { + augSchema = findCorrespondingAugment(getSchema(), choiceCandidate); } - childsInAugment.add(newChild); - } - public AbstractNodeDataWithSchema addChild(final Deque schemas) { - if (schemas.size() == 1) { - final DataSchemaNode childDataSchemaNode = schemas.pop(); - return addChild(childDataSchemaNode); + // looking for existing choice + final Collection childNodes; + if (augSchema != null) { + childNodes = augmentationsToChild.get(augSchema); } else { - DataSchemaNode choiceCandidate = schemas.pop(); - DataSchemaNode caseCandidate = schemas.pop(); - ChoiceNode choiceNode = null; - ChoiceCaseNode caseNode = null; - if (choiceCandidate instanceof ChoiceNode) { - choiceNode = (ChoiceNode) choiceCandidate; - } else { - throw new IllegalArgumentException("Awaited node of type ChoiceNode but was " - + choiceCandidate.getClass().getSimpleName()); - } - - if (caseCandidate instanceof ChoiceCaseNode) { - caseNode = (ChoiceCaseNode) caseCandidate; - } else { - throw new IllegalArgumentException("Awaited node of type ChoiceCaseNode but was " - + caseCandidate.getClass().getSimpleName()); - } - - AugmentationSchema augSchema = null; - if (choiceCandidate.isAugmenting()) { - augSchema = findCorrespondingAugment(getSchema(), choiceCandidate); - } + childNodes = children; + } - // looking for existing choice - List childNodes = Collections.emptyList(); - if (augSchema != null) { - childNodes = augmentationsToChild.get(augSchema); - } else { - childNodes = childs; - } + CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate); + if (caseNodeDataWithSchema == null) { + ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode); + addChild(choiceNodeDataWithSchema); + caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode); + } - CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate); - if (caseNodeDataWithSchema == null) { - ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode); - addChild(choiceNodeDataWithSchema); - caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode); - } + return caseNodeDataWithSchema.addChild(schemas); + } - return caseNodeDataWithSchema.addChild(schemas); + private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) { + SimpleNodeDataWithSchema newChild = null; + if (schema instanceof LeafSchemaNode) { + newChild = new LeafNodeDataWithSchema(schema); + } else if (schema instanceof AnyXmlSchemaNode) { + newChild = new AnyXmlNodeDataWithSchema(schema); + } else { + return null; } + AugmentationSchema augSchema = null; + if (schema.isAugmenting()) { + augSchema = findCorrespondingAugment(getSchema(), schema); + } + if (augSchema != null) { + augmentationsToChild.put(augSchema, newChild); + } else { + addChild(newChild); + } + return newChild; } - private CaseNodeDataWithSchema findChoice(final List childNodes, final DataSchemaNode choiceCandidate, + private CaseNodeDataWithSchema findChoice(final Collection childNodes, final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) { - if (childNodes == null) { - return null; - } - for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) { - if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema - && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) { - CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase(); - if (casePrevious.getSchema().getQName() != caseCandidate.getQName()) { - throw new IllegalArgumentException("Data from case " + caseCandidate.getQName() - + " are specified but other data from case " + casePrevious.getSchema().getQName() - + " were specified erlier. Data aren't from the same case."); + if (childNodes != null) { + for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) { + if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema + && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) { + CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase(); + + Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()), + "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.", + caseCandidate.getQName(), casePrevious.getSchema().getQName()); + + return casePrevious; } - return casePrevious; } } return null; } - public AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) { + AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) { CompositeNodeDataWithSchema newChild; if (schema instanceof ListSchemaNode) { newChild = new ListNodeDataWithSchema(schema); @@ -165,10 +162,10 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { return newChild; } - public void addCompositeChild(final CompositeNodeDataWithSchema newChild) { + void addCompositeChild(final CompositeNodeDataWithSchema newChild) { AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema()); if (augSchema != null) { - addChildToAugmentation(augSchema, newChild); + augmentationsToChild.put(augSchema, newChild); } else { addChild(newChild); } @@ -180,14 +177,22 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } public void addChild(final AbstractNodeDataWithSchema newChild) { - childs.add(newChild); + children.add(newChild); + } + + /** + * Return a hint about how may children we are going to generate. + * @return Size of currently-present node list. + */ + protected final int childSizeHint() { + return children.size(); } /** * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such * node is found then it is returned, else null. */ - protected AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) { + AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) { if (parent instanceof AugmentationTarget) { for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) { DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName()); @@ -200,35 +205,26 @@ class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - for (AbstractNodeDataWithSchema child : childs) { - child.writeToStream(nnStreamWriter); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + for (AbstractNodeDataWithSchema child : children) { + child.write(writer); } - for (Entry> augmentationToChild : augmentationsToChild.entrySet()) { - - final List childsFromAgumentation = augmentationToChild.getValue(); - + for (Entry> augmentationToChild : augmentationsToChild.asMap().entrySet()) { + final Collection childsFromAgumentation = augmentationToChild.getValue(); if (!childsFromAgumentation.isEmpty()) { - nnStreamWriter.startAugmentationNode(toAugmentationIdentifier(augmentationToChild)); + writer.startAugmentationNode(toAugmentationIdentifier(augmentationToChild.getKey())); for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) { - nodeDataWithSchema.writeToStream(nnStreamWriter); + nodeDataWithSchema.write(writer); } - nnStreamWriter.endNode(); + writer.endNode(); } } } - private static AugmentationIdentifier toAugmentationIdentifier( - final Entry> augmentationToChild) { - Collection nodes = augmentationToChild.getKey().getChildNodes(); - Set nodesQNames = new HashSet<>(); - for (DataSchemaNode node : nodes) { - nodesQNames.add(node.getQName()); - } - - return new AugmentationIdentifier(nodesQNames); + private static AugmentationIdentifier toAugmentationIdentifier(final AugmentationSchema schema) { + final Collection qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION); + return new AugmentationIdentifier(ImmutableSet.copyOf(qnames)); } - } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java index c49d71b6fc..b473e9e8a1 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ContainerNodeDataWithSchema.java @@ -7,8 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; - import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; @@ -21,10 +19,10 @@ class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - nnStreamWriter.startContainerNode(provideNodeIdentifier(), UNKNOWN_SIZE); - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + writer.startContainerNode(provideNodeIdentifier(), childSizeHint()); + super.write(writer); + writer.endNode(); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java index 16428cd477..c1a4c31d43 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java @@ -10,19 +10,23 @@ package org.opendaylight.yangtools.yang.data.codec.gson; import com.google.common.base.Preconditions; import com.google.common.base.Strings; import com.google.gson.stream.JsonWriter; - import java.io.IOException; import java.io.Writer; import java.net.URI; import java.util.ArrayDeque; import java.util.Deque; - +import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; /** * This implementation will create JSON output as output stream. @@ -68,6 +72,8 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite private final Deque stack = new ArrayDeque<>(); private final SchemaContext schemaContext; + private final CodecFactory codecs; + private final SchemaTracker tracker; private final Writer writer; private final String indent; @@ -80,12 +86,30 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite this.writer = Preconditions.checkNotNull(writer); Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative"); + if (indentSize != 0) { + indent = Strings.repeat(" ", indentSize); + } else { + indent = null; + } + + this.codecs = CodecFactory.create(schemaContext); + this.tracker = SchemaTracker.create(schemaContext); + } + private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext, final SchemaPath path, + final Writer writer, final int indentSize) { + this.schemaContext = Preconditions.checkNotNull(schemaContext); + this.writer = Preconditions.checkNotNull(writer); + + Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative"); if (indentSize != 0) { indent = Strings.repeat(" ", indentSize); } else { indent = null; } + + this.codecs = CodecFactory.create(schemaContext); + this.tracker = SchemaTracker.create(schemaContext,path); } /** @@ -99,6 +123,17 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0); } + /** + * Create a new stream writer, which writes to the specified {@link Writer}. + * + * @param schemaContext Schema context + * @param writer Output writer + * @return A stream writer instance + */ + public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, SchemaPath path,final Writer writer) { + return new JSONNormalizedNodeStreamWriter(schemaContext, path, writer, 0); + } + /** * Create a new stream writer, which writes to the specified output stream. * @@ -113,15 +148,20 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void leafNode(final NodeIdentifier name, final Object value) throws IOException { + final LeafSchemaNode schema = tracker.leafNode(name); + final Codec codec = codecs.codecFor(schema.getType()); + separateElementFromPreviousElement(); writeJsonIdentifier(name); currentNamespace = stack.peek().getNamespace(); - writeValue(value.toString()); + writeValue(String.valueOf(codec.serialize(value))); separateNextSiblingsWithComma(); } @Override public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startLeafSet(name); + separateElementFromPreviousElement(); stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace())); writeJsonIdentifier(name); @@ -131,13 +171,18 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void leafSetEntryNode(final Object value) throws IOException { + final LeafListSchemaNode schema = tracker.leafSetEntryNode(); + final Codec codec = codecs.codecFor(schema.getType()); + separateElementFromPreviousElement(); - writeValue(value.toString()); + writeValue(String.valueOf(codec.serialize(value))); separateNextSiblingsWithComma(); } @Override public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startContainerNode(name); + separateElementFromPreviousElement(); stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace())); writeJsonIdentifier(name); @@ -147,6 +192,8 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startList(name); + separateElementFromPreviousElement(); stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace())); writeJsonIdentifier(name); @@ -156,6 +203,8 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startListItem(name); + stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace())); separateElementFromPreviousElement(); writeStartObject(); @@ -164,6 +213,8 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startList(name); + separateElementFromPreviousElement(); stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace())); writeJsonIdentifier(name); @@ -174,14 +225,19 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException { - stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace())); + tracker.startListItem(identifier); separateElementFromPreviousElement(); + stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace())); + + writeStartObject(); indentRight(); } @Override public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startListItem(name); + stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace())); separateElementFromPreviousElement(); writeJsonIdentifier(name); @@ -191,16 +247,21 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException { + tracker.startChoiceNode(name); handleInvisibleNode(name.getNodeType().getNamespace()); } @Override public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException { + tracker.startAugmentationNode(identifier); handleInvisibleNode(currentNamespace); } @Override public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException { + final AnyXmlSchemaNode schema = tracker.anyxmlNode(name); + // FIXME: should have a codec based on this :) + separateElementFromPreviousElement(); writeJsonIdentifier(name); currentNamespace = stack.peek().getNamespace(); @@ -210,7 +271,10 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void endNode() throws IOException { - switch (stack.peek().getType()) { + tracker.endNode(); + + final TypeInfo t = stack.pop(); + switch (t.getType()) { case LIST: indentLeft(); newLine(); @@ -224,7 +288,7 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite default: break; } - stack.pop(); + currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace(); separateNextSiblingsWithComma(); } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringIdentityrefCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringIdentityrefCodec.java new file mode 100644 index 0000000000..66d55237a5 --- /dev/null +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringIdentityrefCodec.java @@ -0,0 +1,36 @@ +/* + * 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.yangtools.yang.data.codec.gson; + +import com.google.common.base.Preconditions; + +import java.net.URI; + +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringIdentityrefCodec; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +final class JSONStringIdentityrefCodec extends AbstractModuleStringIdentityrefCodec { + private final SchemaContext context; + + JSONStringIdentityrefCodec(final SchemaContext context) { + this.context = Preconditions.checkNotNull(context); + } + + @Override + protected Module moduleForPrefix(final String prefix) { + return context.findModuleByName(prefix, null); + } + + @Override + protected String prefixForNamespace(final URI namespace) { + final Module module = context.findModuleByNamespaceAndRevision(namespace, null); + return module == null ? null : module.getName(); + } + +} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringInstanceIdentifierCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringInstanceIdentifierCodec.java new file mode 100644 index 0000000000..fc1322fba9 --- /dev/null +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONStringInstanceIdentifierCodec.java @@ -0,0 +1,35 @@ +/* + * 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.yangtools.yang.data.codec.gson; + +import com.google.common.base.Preconditions; + +import java.net.URI; + +import org.opendaylight.yangtools.yang.data.util.AbstractModuleStringInstanceIdentifierCodec; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; + +final class JSONStringInstanceIdentifierCodec extends AbstractModuleStringInstanceIdentifierCodec { + private final SchemaContext context; + + JSONStringInstanceIdentifierCodec(final SchemaContext context) { + this.context = Preconditions.checkNotNull(context); + } + + @Override + protected Module moduleForPrefix(final String prefix) { + return context.findModuleByName(prefix, null); + } + + @Override + protected String prefixForNamespace(final URI namespace) { + final Module module = context.findModuleByNamespaceAndRevision(namespace, null); + return module == null ? null : module.getName(); + } +} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java index 18232ff77e..65c234aae4 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java @@ -10,13 +10,10 @@ package org.opendaylight.yangtools.yang.data.codec.gson; import com.google.common.annotations.Beta; import com.google.common.base.Optional; import com.google.common.base.Preconditions; -import com.google.common.base.Splitter; -import com.google.common.collect.Iterators; import com.google.gson.JsonIOException; import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonToken; import com.google.gson.stream.MalformedJsonException; import java.io.Closeable; @@ -24,24 +21,16 @@ import java.io.EOFException; import java.io.Flushable; import java.io.IOException; import java.net.URI; -import java.security.InvalidParameterException; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Set; -import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestCodecFactory; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil.PrefixMapingFromJson; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.SchemaContextUtils; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceNode; @@ -49,10 +38,9 @@ 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.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; /** * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the @@ -60,19 +48,15 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi */ @Beta public final class JsonParserStream implements Closeable, Flushable { - private static final Splitter COLON_SPLITTER = Splitter.on(':'); - private final Deque namespaces = new ArrayDeque<>(); private final NormalizedNodeStreamWriter writer; - private final SchemaContextUtils utils; - private final RestCodecFactory codecs; + private final CodecFactory codecs; private final SchemaContext schema; private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { this.schema = Preconditions.checkNotNull(schemaContext); - this.utils = SchemaContextUtils.create(schemaContext); this.writer = Preconditions.checkNotNull(writer); - this.codecs = RestCodecFactory.create(utils); + this.codecs = CodecFactory.create(schemaContext); } public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { @@ -90,7 +74,7 @@ public final class JsonParserStream implements Closeable, Flushable { isEmpty = false; CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(schema); read(reader, compositeNodeDataWithSchema); - compositeNodeDataWithSchema.writeToStream(writer); + compositeNodeDataWithSchema.write(writer); return this; // return read(reader); @@ -114,31 +98,26 @@ public final class JsonParserStream implements Closeable, Flushable { } } - public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException { + private final void setValue(final AbstractNodeDataWithSchema parent, final String value) { + Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", parent); + + final Object translatedValue = translateValueByType(value, parent.getSchema()); + ((SimpleNodeDataWithSchema) parent).setValue(translatedValue); + } - final JsonToken peek = in.peek(); - Optional value = Optional.absent(); - switch (peek) { + public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException { + switch (in.peek()) { case STRING: case NUMBER: - value = Optional.of(in.nextString()); + setValue(parent, in.nextString()); break; case BOOLEAN: - value = Optional.of(Boolean.toString(in.nextBoolean())); + setValue(parent, Boolean.toString(in.nextBoolean())); break; case NULL: in.nextNull(); - value = Optional.of((String) null); - break; - default: + setValue(parent, null); break; - } - if (value.isPresent()) { - final Object translatedValue = translateValueByType(value.get(), parent.getSchema()); - ((SimpleNodeDataWithSchema) parent).setValue(translatedValue); - } - - switch (peek) { case BEGIN_ARRAY: in.beginArray(); while (in.hasNext()) { @@ -189,6 +168,7 @@ public final class JsonParserStream implements Closeable, Flushable { case NAME: case END_OBJECT: case END_ARRAY: + break; } } @@ -198,21 +178,7 @@ public final class JsonParserStream implements Closeable, Flushable { return value; } - final Object inputValue; - if (typeDefinition instanceof IdentityrefTypeDefinition) { - inputValue = valueAsIdentityRef(value); - } else if (typeDefinition instanceof InstanceIdentifierTypeDefinition) { - inputValue = valueAsInstanceIdentifier(value); - } else { - inputValue = value; - } - - // FIXME: extract this as a cacheable context? - final Codec codec = codecs.codecFor(typeDefinition); - if (codec == null) { - return null; - } - return codec.deserialize(inputValue); + return codecs.codecFor(typeDefinition).deserialize(value); } private static TypeDefinition typeDefinition(final DataSchemaNode node) { @@ -235,48 +201,6 @@ public final class JsonParserStream implements Closeable, Flushable { return baseType; } - private static Object valueAsInstanceIdentifier(final String value) { - // it could be instance-identifier Built-In Type - if (!value.isEmpty() && value.charAt(0) == '/') { - IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson()); - if (resolvedValue != null) { - return resolvedValue; - } - } - throw new InvalidParameterException("Value for instance-identifier doesn't have correct format"); - } - - private static IdentityValuesDTO valueAsIdentityRef(final String value) { - // it could be identityref Built-In Type - URI namespace = getNamespaceFor(value); - if (namespace != null) { - return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value); - } - throw new InvalidParameterException("Value for identityref has to be in format moduleName:localName."); - } - - private static URI getNamespaceFor(final String jsonElementName) { - final Iterator it = COLON_SPLITTER.split(jsonElementName).iterator(); - - // The string needs to me in form "moduleName:localName" - if (it.hasNext()) { - final String maybeURI = it.next(); - if (Iterators.size(it) == 1) { - return URI.create(maybeURI); - } - } - - return null; - } - - private static String getLocalNameFor(final String jsonElementName) { - final Iterator it = COLON_SPLITTER.split(jsonElementName).iterator(); - - // The string needs to me in form "moduleName:localName" - final String ret = Iterators.get(it, 1, null); - return ret != null && !it.hasNext() ? ret : jsonElementName; - } - private void removeNamespace() { namespaces.pop(); } @@ -294,14 +218,16 @@ public final class JsonParserStream implements Closeable, Flushable { } private NamespaceAndName resolveNamespace(final String childName) { - int lastIndexOfColon = childName.lastIndexOf(":"); + int lastIndexOfColon = childName.lastIndexOf(':'); String moduleNamePart = null; String nodeNamePart = null; URI namespace = null; if (lastIndexOfColon != -1) { moduleNamePart = childName.substring(0, lastIndexOfColon); nodeNamePart = childName.substring(lastIndexOfColon + 1); - namespace = utils.findNamespaceByModuleName(moduleNamePart); + + final Module m = schema.findModuleByName(moduleNamePart, null); + namespace = m == null ? null : m.getNamespace(); } else { nodeNamePart = childName; } @@ -315,14 +241,14 @@ public final class JsonParserStream implements Closeable, Flushable { } /** - * Returns stack of schema nodes via which it was necessary to prass to get schema node with specified + * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified * {@code childName} and {@code namespace} * * @param dataSchemaNode * @param childName * @param namespace - * @return stack of schema nodes via which it was passed through. If found schema node is dirrect child then stack - * contains only one node. If it is found under choice and case then stack should conains 2*n+1 element + * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack + * contains only one node. If it is found under choice and case then stack should contains 2*n+1 element * (where n is number of choices through it was passed) */ private Deque findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode, diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java index e53367c637..7063be6753 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListEntryNodeDataWithSchema.java @@ -18,7 +18,7 @@ class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - nnStreamWriter.leafSetEntryNode(getValue()); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + writer.leafSetEntryNode(getValue()); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java index 8b23acdb9f..20a607d5fe 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafListNodeDataWithSchema.java @@ -7,8 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; - import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; @@ -20,9 +18,9 @@ class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - nnStreamWriter.startLeafSet(provideNodeIdentifier(), UNKNOWN_SIZE); - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + writer.startLeafSet(provideNodeIdentifier(), childSizeHint()); + super.write(writer); + writer.endNode(); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java index c63422af73..fc8c814c3f 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/LeafNodeDataWithSchema.java @@ -19,8 +19,8 @@ class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - nnStreamWriter.leafNode(provideNodeIdentifier(), getValue()); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + writer.leafNode(provideNodeIdentifier(), getValue()); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java index b08add8a36..3e877bbfd7 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListEntryNodeDataWithSchema.java @@ -7,13 +7,17 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; +import com.google.common.base.Function; +import com.google.common.base.Preconditions; +import com.google.common.collect.Maps; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nonnull; + import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; @@ -22,6 +26,12 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { + private static final Function VALUE_FUNCTION = new Function() { + @Override + public Object apply(@Nonnull final SimpleNodeDataWithSchema input) { + return input.getValue(); + } + }; private final Map qNameToKeys = new HashMap<>(); @@ -31,7 +41,7 @@ class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { @Override public void addChild(final AbstractNodeDataWithSchema newChild) { - DataSchemaNode childSchema = newChild.getSchema(); + final DataSchemaNode childSchema = newChild.getSchema(); if (childSchema instanceof LeafSchemaNode && isPartOfKey((LeafSchemaNode) childSchema)) { qNameToKeys.put(childSchema.getQName(), (SimpleNodeDataWithSchema)newChild); } @@ -49,29 +59,20 @@ class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - int keyCount = ((ListSchemaNode) getSchema()).getKeyDefinition().size(); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + final int keyCount = ((ListSchemaNode) getSchema()).getKeyDefinition().size(); if (keyCount == 0) { - nnStreamWriter.startUnkeyedListItem(provideNodeIdentifier(), UNKNOWN_SIZE); - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); - } else if (keyCount == qNameToKeys.size()) { - nnStreamWriter.startMapEntryNode(provideNodeIdentifierWithPredicates(), UNKNOWN_SIZE); - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); - } else { - throw new IllegalStateException("Some of keys of " + getSchema().getQName() + " are missing in input."); + writer.startUnkeyedListItem(provideNodeIdentifier(), childSizeHint()); + super.write(writer); + writer.endNode(); + return; } - } - - private NodeIdentifierWithPredicates provideNodeIdentifierWithPredicates() { - Map qNameToPredicateValues = new HashMap<>(); - for (SimpleNodeDataWithSchema simpleNodeDataWithSchema : qNameToKeys.values()) { - qNameToPredicateValues.put(simpleNodeDataWithSchema.getSchema().getQName(), simpleNodeDataWithSchema.getValue()); - } - - return new NodeIdentifierWithPredicates(getSchema().getQName(), qNameToPredicateValues); + Preconditions.checkState(keyCount == qNameToKeys.size(), "Input is missing some of the keys of %s", getSchema().getQName()); + writer.startMapEntryNode( + new NodeIdentifierWithPredicates(getSchema().getQName(), Maps.transformValues(qNameToKeys, VALUE_FUNCTION)), + childSizeHint()); + super.write(writer); + writer.endNode(); } - } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java index 612c386e23..d21cd9a77a 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/ListNodeDataWithSchema.java @@ -7,8 +7,6 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; - import java.io.IOException; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; @@ -22,14 +20,14 @@ class ListNodeDataWithSchema extends CompositeNodeDataWithSchema { } @Override - protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException { - if (!((ListSchemaNode) getSchema()).getKeyDefinition().isEmpty()) { - nnStreamWriter.startMapNode(provideNodeIdentifier(), UNKNOWN_SIZE); + public void write(final NormalizedNodeStreamWriter writer) throws IOException { + if (((ListSchemaNode) getSchema()).getKeyDefinition().isEmpty()) { + writer.startUnkeyedList(provideNodeIdentifier(), childSizeHint()); } else { - nnStreamWriter.startUnkeyedList(provideNodeIdentifier(), UNKNOWN_SIZE); + writer.startMapNode(provideNodeIdentifier(), childSizeHint()); } - super.writeToStream(nnStreamWriter); - nnStreamWriter.endNode(); + super.write(writer); + writer.endNode(); } } diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java deleted file mode 100644 index 407d8661ab..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/AbstractCodecImpl.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import com.google.common.base.Preconditions; - -import java.net.URI; - -import org.opendaylight.yangtools.yang.model.api.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -abstract class AbstractCodecImpl { - private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecImpl.class); - private final SchemaContextUtils schema; - - protected AbstractCodecImpl(final SchemaContextUtils schema) { - this.schema = Preconditions.checkNotNull(schema); - } - - protected final SchemaContextUtils getSchema() { - return schema; - } - - protected final Module getModuleByNamespace(final String namespace) { - URI validNamespace = resolveValidNamespace(namespace); - - Module module = schema.findModuleByNamespace(validNamespace); - if (module == null) { - LOG.info("Module for namespace " + validNamespace + " wasn't found."); - return null; - } - return module; - } - - protected final URI resolveValidNamespace(final String namespace) { - URI validNamespace = schema.findNamespaceByModuleName(namespace); - if (validNamespace == null) { - validNamespace = URI.create(namespace); - } - - return validNamespace; - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java deleted file mode 100644 index 30ba2a0f9c..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityValuesDTO.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import com.google.common.annotations.Beta; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * This class is implementation-internal and subject to change. Please do not use it. - */ -@Beta -public final class IdentityValuesDTO { - - private final List elementData = new ArrayList<>(); - private final String originValue; - - public IdentityValuesDTO(final String namespace, final String value, final String prefix, final String originValue) { - elementData.add(new IdentityValue(namespace, value, prefix)); - this.originValue = originValue; - } - - public IdentityValuesDTO(final String originValue) { - this.originValue = originValue; - } - - public IdentityValuesDTO() { - originValue = null; - } - - public void add(final String namespace, final String value, final String prefix) { - elementData.add(new IdentityValue(namespace, value, prefix)); - } - - public void add(final IdentityValue identityValue) { - elementData.add(identityValue); - } - - public List getValuesWithNamespaces() { - return Collections.unmodifiableList(elementData); - } - - @Override - public String toString() { - return elementData.toString(); - } - - public String getOriginValue() { - return originValue; - } - - public static final class IdentityValue { - - private final String namespace; - private final String value; - private final String prefix; - private List predicates; - - public IdentityValue(final String namespace, final String value, final String prefix) { - this.namespace = namespace; - this.value = value; - this.prefix = prefix; - } - - public String getNamespace() { - return namespace; - } - - public String getValue() { - return value; - } - - public String getPrefix() { - return prefix; - } - - public List getPredicates() { - if (predicates == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(predicates); - } - - public void setPredicates(final List predicates) { - this.predicates = predicates; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (namespace != null) { - sb.append(namespace); - } - if (prefix != null) { - sb.append("(").append(prefix).append(")"); - } - if (value != null) { - sb.append(" - ").append(value); - } - if (predicates != null && !predicates.isEmpty()) { - for (Predicate predicate : predicates) { - sb.append("["); - predicate.toString(); - sb.append("]"); - } - } - return sb.toString(); - } - - } - - public static final class Predicate { - - private final IdentityValue name; - private final String value; - - public Predicate(final IdentityValue name, final String value) { - super(); - this.name = name; - this.value = value; - } - - public IdentityValue getName() { - return name; - } - - public String getValue() { - return value; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (name != null) { - sb.append(name.toString()); - } - if (value != null) { - sb.append("=").append(value); - } - return sb.toString(); - } - - public boolean isLeafList() { - return name == null ? true : false; - } - - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java deleted file mode 100644 index a8c8b8d6f5..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/IdentityrefCodecImpl.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class IdentityrefCodecImpl extends AbstractCodecImpl implements IdentityrefCodec { - private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class); - - IdentityrefCodecImpl(final SchemaContextUtils schema) { - super(schema); - } - - @Override - public IdentityValuesDTO serialize(final QName data) { - return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(), null); - } - - @Override - public QName deserialize(final IdentityValuesDTO data) { - IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); - Module module = getModuleByNamespace(valueWithNamespace.getNamespace()); - if (module == null) { - LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace()); - LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace)); - return null; - } - - return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue()); - } - -} \ No newline at end of file diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java deleted file mode 100644 index 455bdf4edb..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/InstanceIdentifierCodecImpl.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import java.net.URI; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.opendaylight.yangtools.yang.common.QName; -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.YangInstanceIdentifier.NodeWithValue; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; -import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate; -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.api.Module; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -class InstanceIdentifierCodecImpl extends AbstractCodecImpl implements InstanceIdentifierCodec { - private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class); - - InstanceIdentifierCodecImpl(final SchemaContextUtils schema) { - super(schema); - } - - @Override - public IdentityValuesDTO serialize(final YangInstanceIdentifier data) { - IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(); - for (PathArgument pathArgument : data.getPathArguments()) { - IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType()); - if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) { - List predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument) - .getKeyValues()); - identityValue.setPredicates(predicates); - } else if (pathArgument instanceof NodeWithValue && identityValue != null) { - List predicates = new ArrayList<>(); - String value = String.valueOf(((NodeWithValue) pathArgument).getValue()); - predicates.add(new Predicate(null, value)); - identityValue.setPredicates(predicates); - } - identityValuesDTO.add(identityValue); - } - return identityValuesDTO; - } - - @Override - public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) { - List result = new ArrayList(); - IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0); - Module module = getModuleByNamespace(valueWithNamespace.getNamespace()); - if (module == null) { - LOG.info("Module by namespace '{}' of first node in instance-identiefier was not found.", - valueWithNamespace.getNamespace()); - LOG.info("Instance-identifier will be translated as NULL for data - {}", - String.valueOf(valueWithNamespace.getValue())); - return null; - } - - DataNodeContainer parentContainer = module; - List identities = data.getValuesWithNamespaces(); - for (int i = 0; i < identities.size(); i++) { - IdentityValue identityValue = identities.get(i); - URI validNamespace = resolveValidNamespace(identityValue.getNamespace()); - DataSchemaNode node = getSchema().findInstanceDataChildByNameAndNamespace( - parentContainer, identityValue.getValue(), validNamespace); - if (node == null) { - LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes()); - LOG.info("Instance-identifier will be translated as NULL for data - {}", - String.valueOf(identityValue.getValue())); - return null; - } - QName qName = node.getQName(); - PathArgument pathArgument = null; - if (identityValue.getPredicates().isEmpty()) { - pathArgument = new NodeIdentifier(qName); - } else { - if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry - Predicate leafListPredicate = identityValue.getPredicates().get(0); - if (!leafListPredicate.isLeafList()) { - LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\""); - LOG.info("Instance-identifier will be translated as NULL for data - {}", - String.valueOf(identityValue.getValue())); - return null; - } - pathArgument = new NodeWithValue(qName, leafListPredicate.getValue()); - } else if (node instanceof ListSchemaNode) { // predicates are keys of list - DataNodeContainer listNode = (DataNodeContainer) node; - Map predicatesMap = new HashMap<>(); - for (Predicate predicate : identityValue.getPredicates()) { - validNamespace = resolveValidNamespace(predicate.getName().getNamespace()); - DataSchemaNode listKey = getSchema() - .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(), - validNamespace); - predicatesMap.put(listKey.getQName(), predicate.getValue()); - } - pathArgument = new NodeIdentifierWithPredicates(qName, predicatesMap); - } else { - LOG.info("Node {} is not List or Leaf-list.", node); - LOG.info("Instance-identifier will be translated as NULL for data - {}", - String.valueOf(identityValue.getValue())); - return null; - } - } - result.add(pathArgument); - if (i < identities.size() - 1) { // last element in instance-identifier can be other than - // DataNodeContainer - if (node instanceof DataNodeContainer) { - parentContainer = (DataNodeContainer) node; - } else { - LOG.info("Node {} isn't instance of DataNodeContainer", node); - LOG.info("Instance-identifier will be translated as NULL for data - {}", - String.valueOf(identityValue.getValue())); - return null; - } - } - } - - return result.isEmpty() ? null : YangInstanceIdentifier.create(result); - } - - private static List keyValuesToPredicateList(final Map keyValues) { - List result = new ArrayList<>(keyValues.size()); - for (Map.Entry e : keyValues.entrySet()) { - result.add(new Predicate(qNameToIdentityValue(e.getKey()), String.valueOf(e.getValue()))); - } - return result; - } - - private static IdentityValue qNameToIdentityValue(final QName qName) { - if (qName != null) { - // FIXME: the prefix here is completely arbitrary - return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix()); - } - return null; - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java deleted file mode 100644 index c6d8baf9fe..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/LeafrefCodecImpl.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec; - -class LeafrefCodecImpl implements LeafrefCodec { - - @Override - public String serialize(final Object data) { - return String.valueOf(data); - } - - @Override - public Object deserialize(final String data) { - return data; - } - -} \ No newline at end of file diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java deleted file mode 100644 index abe7cd2e63..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/ObjectCodec.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import org.opendaylight.yangtools.concepts.Codec; -import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; -import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@SuppressWarnings("rawtypes") -final class ObjectCodec extends AbstractCodecImpl implements Codec { - public static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl(); - private static final Logger LOG = LoggerFactory.getLogger(RestCodecFactory.class); - private final Codec instanceIdentifier; - private final Codec identityrefCodec; - private final TypeDefinition type; - - ObjectCodec(final SchemaContextUtils schema, final TypeDefinition typeDefinition) { - super(schema); - type = RestUtil.resolveBaseTypeFrom(typeDefinition); - if (type instanceof IdentityrefTypeDefinition) { - identityrefCodec = new IdentityrefCodecImpl(schema); - } else { - identityrefCodec = null; - } - if (type instanceof InstanceIdentifierTypeDefinition) { - instanceIdentifier = new InstanceIdentifierCodecImpl(schema); - } else { - instanceIdentifier = null; - } - } - - @SuppressWarnings("unchecked") - @Override - public Object deserialize(final Object input) { - try { - if (type instanceof IdentityrefTypeDefinition) { - if (input instanceof IdentityValuesDTO) { - return identityrefCodec.deserialize(input); - } - LOG.debug("Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of - {}", - input == null ? "null" : input.getClass(), String.valueOf(input)); - return null; - } else if (type instanceof LeafrefTypeDefinition) { - if (input instanceof IdentityValuesDTO) { - return LEAFREF_DEFAULT_CODEC.deserialize(((IdentityValuesDTO) input).getOriginValue()); - } - return LEAFREF_DEFAULT_CODEC.deserialize(input); - } else if (type instanceof InstanceIdentifierTypeDefinition) { - if (input instanceof IdentityValuesDTO) { - return instanceIdentifier.deserialize(input); - } - LOG.info( - "Value is not instance of InstanceIdentifierTypeDefinition but is {}. Therefore NULL is used as translation of - {}", - input == null ? "null" : input.getClass(), String.valueOf(input)); - return null; - } else { - TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec - .from(type); - if (typeAwarecodec != null) { - if (input instanceof IdentityValuesDTO) { - return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue()); - } - return typeAwarecodec.deserialize(String.valueOf(input)); - } else { - LOG.debug("Codec for type \"" + type.getQName().getLocalName() - + "\" is not implemented yet."); - return null; - } - } - } catch (ClassCastException e) { - // TODO remove this catch when everyone use codecs - LOG.error("ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input), - e); - return null; - } - } - - @SuppressWarnings("unchecked") - @Override - public Object serialize(final Object input) { - try { - if (type instanceof IdentityrefTypeDefinition) { - return identityrefCodec.serialize(input); - } else if (type instanceof LeafrefTypeDefinition) { - return LEAFREF_DEFAULT_CODEC.serialize(input); - } else if (type instanceof InstanceIdentifierTypeDefinition) { - return instanceIdentifier.serialize(input); - } else { - TypeDefinitionAwareCodec> typeAwarecodec = TypeDefinitionAwareCodec - .from(type); - if (typeAwarecodec != null) { - return typeAwarecodec.serialize(input); - } else { - LOG.debug("Codec for type \"" + type.getQName().getLocalName() - + "\" is not implemented yet."); - return null; - } - } - } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs - LOG.error( - "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input), - e); - return input; - } - } - -} \ No newline at end of file diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java deleted file mode 100644 index 94bba92243..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestCodecFactory.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import com.google.common.annotations.Beta; -import com.google.common.base.Preconditions; - -import org.opendaylight.yangtools.concepts.Codec; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; - -/** - * This class is implementation-internal and subject to change. Please do not use it. - */ -@Beta -public final class RestCodecFactory { - private final SchemaContextUtils utils; - - private RestCodecFactory(final SchemaContextUtils utils) { - this.utils = Preconditions.checkNotNull(utils); - } - - public static RestCodecFactory create(final SchemaContextUtils utils) { - return new RestCodecFactory(utils); - } - - public final Codec codecFor(final TypeDefinition typeDefinition) { - // FIXME: implement loadingcache - return new ObjectCodec(utils, typeDefinition); - } -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java deleted file mode 100644 index c3d002c4bb..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/RestUtil.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - - -import com.google.common.annotations.Beta; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.xml.stream.events.StartElement; - -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue; -import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate; -import org.opendaylight.yangtools.yang.model.api.TypeDefinition; - -/** - * This class is implementation-internal and subject to change. Please do not use it. - */ -@Beta -public final class RestUtil { - - // FIXME: BUG-1275: this is code duplicates data.impl.codec - - public static final String SQUOTE = "'"; - public static final String DQUOTE = "\""; - private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]"); - - public final static TypeDefinition resolveBaseTypeFrom(final TypeDefinition type) { - TypeDefinition superType = type; - while (superType.getBaseType() != null) { - superType = superType.getBaseType(); - } - return superType; - } - - public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) { - String valueTrimmed = value.trim(); - if (!valueTrimmed.startsWith("/")) { - return null; - } - String[] xPathParts = valueTrimmed.split("/"); - if (xPathParts.length < 2) { // must be at least "/pr:node" - return null; - } - IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value); - for (int i = 1; i < xPathParts.length; i++) { - String xPathPartTrimmed = xPathParts[i].trim(); - - String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed); - IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap); - if (identityValue == null) { - return null; - } - - List predicates = toPredicates(xPathPartTrimmed, prefixMap); - if (predicates == null) { - return null; - } - identityValue.setPredicates(predicates); - - identityValuesDTO.add(identityValue); - } - return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO; - } - - private static String getIdAndPrefixAsStr(final String pathPart) { - int predicateStartIndex = pathPart.indexOf("["); - return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex); - } - - private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) { - String xPathPartTrimmed = xPathPart.trim(); - if (xPathPartTrimmed.isEmpty()) { - return null; - } - String[] prefixAndIdentifier = xPathPartTrimmed.split(":"); - // it is not "prefix:value" - if (prefixAndIdentifier.length != 2) { - return null; - } - String prefix = prefixAndIdentifier[0].trim(); - String identifier = prefixAndIdentifier[1].trim(); - if (prefix.isEmpty() || identifier.isEmpty()) { - return null; - } - String namespace = prefixMap.getNamespace(prefix); - return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix); - } - - private static List toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) { - List result = new ArrayList<>(); - List predicates = new ArrayList<>(); - Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr); - while (matcher.find()) { - predicates.add(matcher.group(1).trim()); - } - for (String predicate : predicates) { - int indexOfEqualityMark = predicate.indexOf("="); - if (indexOfEqualityMark != -1) { - String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1)); - if (predicate.startsWith(".")) { // it is leaf-list - if (predicateValue == null) { - return null; - } - result.add(new Predicate(null, predicateValue)); - } else { - IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap); - if (identityValue == null || predicateValue == null) { - return null; - } - result.add(new Predicate(identityValue, predicateValue)); - } - } - } - return result; - } - - private static String toPredicateValue(final String predicatedValue) { - String predicatedValueTrimmed = predicatedValue.trim(); - if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE)) - && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) { - return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1); - } - return null; - } - - public interface PrefixesMaping { - public String getNamespace(String prefix); - } - - public static class PrefixMapingFromXml implements PrefixesMaping { - StartElement startElement = null; - - public PrefixMapingFromXml(final StartElement startElement) { - this.startElement = startElement; - } - - @Override - public String getNamespace(final String prefix) { - return startElement.getNamespaceContext().getNamespaceURI(prefix); - } - } - - public static class PrefixMapingFromJson implements PrefixesMaping { - - @Override - public String getNamespace(final String prefix) { - return prefix; - } - } - -} diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java deleted file mode 100644 index 5e8f6f1e1e..0000000000 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/helpers/SchemaContextUtils.java +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.yangtools.yang.data.codec.gson.helpers; - -import com.google.common.annotations.Beta; -import com.google.common.base.Function; -import com.google.common.base.Objects; -import com.google.common.base.Preconditions; -import com.google.common.base.Predicate; -import com.google.common.collect.Iterables; - -import java.net.URI; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; - -import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; -import org.opendaylight.yangtools.yang.model.api.ChoiceNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; - -/** - * This class is implementation-internal and subject to change. Please do not use it. - */ -@Beta -public final class SchemaContextUtils { - private final SchemaContext schemaContext; - - private SchemaContextUtils(final SchemaContext schemaContext) { - this.schemaContext = Preconditions.checkNotNull(schemaContext); - } - - public static SchemaContextUtils create(final SchemaContext schemaContext) { - return new SchemaContextUtils(schemaContext); - } - - public URI findNamespaceByModuleName(final String moduleName) { - final Module module = this.findModuleByName(moduleName); - return module == null ? null : module.getNamespace(); - } - - - public Module findModuleByName(final String moduleName) { - checkPreconditions(); - Preconditions.checkArgument(moduleName != null && !moduleName.isEmpty()); - return schemaContext.findModuleByName(moduleName, null); - } - - public Module findModuleByNamespace(final URI namespace) { - this.checkPreconditions(); - Preconditions.checkArgument(namespace != null); - return schemaContext.findModuleByNamespaceAndRevision(namespace, null); - } - - private void checkPreconditions() { - if (schemaContext == null) { - throw new IllegalStateException("Schema context isn't set."); - } - } - - public DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name, - final URI namespace) { - Preconditions. checkNotNull(namespace); - - final List potentialSchemaNodes = findInstanceDataChildrenByName(container, name); - - Predicate filter = new Predicate() { - @Override - public boolean apply(final DataSchemaNode node) { - return Objects.equal(node.getQName().getNamespace(), namespace); - } - }; - - Iterable result = Iterables.filter(potentialSchemaNodes, filter); - return Iterables.getFirst(result, null); - } - - public List findInstanceDataChildrenByName(final DataNodeContainer container, final String name) { - Preconditions. checkNotNull(container); - Preconditions. checkNotNull(name); - - List instantiatedDataNodeContainers = new ArrayList(); - collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name); - return instantiatedDataNodeContainers; - } - - private void collectInstanceDataNodeContainers(final List potentialSchemaNodes, - final DataNodeContainer container, final String name) { - - Predicate filter = new Predicate() { - @Override - public boolean apply(final DataSchemaNode node) { - return Objects.equal(node.getQName().getLocalName(), name); - } - }; - - Iterable nodes = Iterables.filter(container.getChildNodes(), filter); - - // Can't combine this loop with the filter above because the filter is - // lazily-applied by Iterables.filter. - for (final DataSchemaNode potentialNode : nodes) { - if (isInstantiatedDataSchema(potentialNode)) { - potentialSchemaNodes.add(potentialNode); - } - } - - Iterable choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceNode.class); - Iterable> map = Iterables.transform(choiceNodes, CHOICE_FUNCTION); - - final Iterable allCases = Iterables. concat(map); - for (final ChoiceCaseNode caze : allCases) { - collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name); - } - } - - public boolean isInstantiatedDataSchema(final DataSchemaNode node) { - return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode - || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode - || node instanceof AnyXmlSchemaNode; - } - - private final Function> CHOICE_FUNCTION = new Function>() { - @Override - public Set apply(final ChoiceNode node) { - return node.getCases(); - } - }; - -} diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java index f51179e6cc..534b3bdb44 100644 --- a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java +++ b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/StreamToNormalizedNodeTest.java @@ -8,6 +8,7 @@ package org.opendaylight.yangtools.yang.data.codec.gson; import com.google.gson.stream.JsonReader; + import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; @@ -19,10 +20,10 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; + import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; -import org.opendaylight.yangtools.yang.common.QName; -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.ContainerNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; @@ -30,9 +31,8 @@ import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; import org.opendaylight.yangtools.yang.data.api.schema.stream.LoggingNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter; -import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser; import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl; @@ -77,17 +77,17 @@ public class StreamToNormalizedNodeTest { * * @throws IOException */ + @Ignore @Test public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException { /* * This is the parsing part */ // This is where we will output the nodes - final NormalizedNodeContainerBuilder> parent = - Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create("dummy", "2014-12-31", "dummy"))); + NormalizedNodeResult result = new NormalizedNodeResult(); // StreamWriter which attaches NormalizedNode under parent - final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(parent); + final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); // JSON -> StreamWriter parser try (JsonParserStream handler = JsonParserStream.create(streamWriter, schemaContext)) { @@ -95,14 +95,14 @@ public class StreamToNormalizedNodeTest { } // Finally build the node - final NormalizedNode parsedData = parent.build(); + final NormalizedNode parsedData = result.getResult(); LOG.debug("Parsed NormalizedNodes: {}", parsedData); /* * This is the serialization part. */ // We want to write the first child out - final DataContainerChild firstChild = ((ContainerNode) parsedData).getValue().iterator().next(); + final DataContainerChild firstChild = (DataContainerChild) parsedData; LOG.debug("Serializing first child: {}", firstChild); // String holder diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json b/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json index 8ecaa37e4f..fd4538833b 100644 --- a/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json +++ b/yang/yang-data-codec-gson/src/test/resources/complexjson/complex-json.json @@ -5,37 +5,37 @@ "anyxml-in-data":"foo" } ], - + "lf13-any":{ "anyxml-in-data":"foo" }, - + "lf14-any":"anyxml data", - + "lflst11":["lflst11 value1","lflst11 value2"], - + "lst11":[ - { + { "key111":"key111 value", "lf112":"/complexjson:cont1/complexjson:lflst11", "lf113":"lf113 value", "lf111":"lf111 value" } - ], + ], "lf11" : "453", "lf12_1" : "lf12 value", - "lf13" : "lf13 value", + "lf13" : "lf13 value", "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation", "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation", "lf15_11" : "one two", "lf15_12" : "complexjson:lf11", "lf15_21" : "lf15_21 value", - "lf17" : "lf17 value", - + "lf17" : "lf17 value", + "lst12":[ { "lf121":"lf121 value" } ] } -} \ No newline at end of file +} diff --git a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang index 6c07d656da..de0858d667 100644 --- a/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang +++ b/yang/yang-data-codec-gson/src/test/resources/complexjson/yang/complexjson.yang @@ -2,26 +2,26 @@ module complexjson { namespace "ns:complex:json"; prefix cj; - revision "2014-08-11" { + revision "2014-08-11" { } - - + + identity ident; - + container cont1 { - + anyxml lf12-any; anyxml lf13-any; anyxml lf14-any; - + leaf lf11 { type int32; } - + leaf-list lflst11 { type string; } - + list lst11 { key "key111 lf111"; leaf key111 { @@ -37,7 +37,7 @@ module complexjson { type string; } } - + list lst12 { leaf lf121 { type string; @@ -46,8 +46,8 @@ module complexjson { type string; } } - - + + choice choc11 { case c11A { leaf lf13 { @@ -64,77 +64,74 @@ module complexjson { } } } - - + augment "/cont1/choc12" { case c12B { leaf lf17 { type string; } } - } - - + } + augment "/cont1" { container cont11 { leaf lf111 { type string; } - } + } } - + augment "/cont1" { leaf lf12_1 { - type string; - } + type string; + } leaf lf12_2 { type string; } } - + augment "/cont1" { leaf lf12_3 { - type string; - } + type string; + } } - - + augment "/cont1/choc11" { case c11B { leaf lf14_1 { - type string; - } + type string; + } } } - + augment "/cont1/choc11" { case c11C { leaf lf14_2 { - type string; - } + type string; + } } } - + augment "/cont1/choc11/c11A" { leaf lf15_11 { - type bits { - bit one; - bit two; - bit three; - } - } + type bits { + bit one; + bit two; + bit three; + } + } leaf lf15_12 { - type identityref { - base ident; - } - } - + type identityref { + base ident; + } + } + } - + augment "/cont1/choc11/c11A" { leaf lf15_21 { - type string; - } + type string; + } } } diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/InstanceIdentifierCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/InstanceIdentifierCodec.java index 5f85b4f0a2..de7949b212 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/InstanceIdentifierCodec.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/InstanceIdentifierCodec.java @@ -9,12 +9,12 @@ package org.opendaylight.yangtools.yang.data.impl.codec; import org.opendaylight.yangtools.yang.binding.BindingCodec; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -public interface InstanceIdentifierCodec extends BindingCodec> { - +public interface InstanceIdentifierCodec extends BindingCodec> { @Override - org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier serialize(InstanceIdentifier input); + YangInstanceIdentifier serialize(InstanceIdentifier input); @Override - InstanceIdentifier deserialize(org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier input); + InstanceIdentifier deserialize(YangInstanceIdentifier input); } diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java new file mode 100644 index 0000000000..4a8232e180 --- /dev/null +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/SchemaTracker.java @@ -0,0 +1,192 @@ +/* + * 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.yangtools.yang.data.impl.codec; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import java.io.IOException; +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import javax.xml.stream.XMLStreamWriter; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.SchemaUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.base.AugmentationSchemaProxy; +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.ChoiceNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Utility class for tracking the underlying state of the underlying + * schema node. + */ +@Beta +public final class SchemaTracker { + private static final Logger LOG = LoggerFactory.getLogger(SchemaTracker.class); + private final Deque schemaStack = new ArrayDeque<>(); + private final DataNodeContainer root; + + private SchemaTracker(final SchemaContext context, final SchemaPath path) { + DataNodeContainer current = Preconditions.checkNotNull(context); + for (QName qname : path.getPathFromRoot()) { + final DataSchemaNode child = current.getDataChildByName(qname); + Preconditions.checkArgument(child instanceof DataNodeContainer); + current = (DataNodeContainer) child; + } + + this.root = current; + } + + /** + * Create a new writer with the specified context as its root. + * + * @param writer Output {@link XMLStreamWriter} + * @param context Associated {@link SchemaContext}. + * @return A new {@link NormalizedNodeStreamWriter} + */ + public static SchemaTracker create(final SchemaContext context) { + return create(context, SchemaPath.ROOT); + } + + /** + * Create a new writer with the specified context and rooted in the specified schema path + * + * @param writer Output {@link XMLStreamWriter} + * @param context Associated {@link SchemaContext}. + * + * @return A new {@link NormalizedNodeStreamWriter} + */ + public static SchemaTracker create(final SchemaContext context, final SchemaPath path) { + return new SchemaTracker(context, path); + } + + public Object getParent() { + if (schemaStack.isEmpty()) { + return root; + } + return schemaStack.peek(); + } + + private SchemaNode getSchema(final PathArgument name) { + final Object parent = getParent(); + SchemaNode schema = null; + final QName qname = name.getNodeType(); + if(parent instanceof DataNodeContainer) { + schema = ((DataNodeContainer)parent).getDataChildByName(qname); + + } else if(parent instanceof ChoiceNode) { + for(ChoiceCaseNode caze : ((ChoiceNode) parent).getCases()) { + DataSchemaNode potential = caze.getDataChildByName(qname); + if(potential != null) { + schema = potential; + break; + } + } + } else { + throw new IllegalStateException("Unsupported schema type "+ parent.getClass() +" on stack."); + } + Preconditions.checkArgument(schema != null, "Could not find schema for node %s in %s", qname, parent); + return schema; + } + + public void startList(final NodeIdentifier name) { + final SchemaNode schema = getSchema(name); + Preconditions.checkArgument(schema instanceof ListSchemaNode, "Node %s is not a list", schema.getPath()); + schemaStack.push(schema); + } + + public void startListItem(final PathArgument name) throws IOException { + final Object schema = getParent(); + Preconditions.checkArgument(schema instanceof ListSchemaNode, "List item is not appropriate"); + schemaStack.push(schema); + } + + public LeafSchemaNode leafNode(final NodeIdentifier name) throws IOException { + final SchemaNode schema = getSchema(name); + + Preconditions.checkArgument(schema instanceof LeafSchemaNode, "Node %s is not a leaf", schema.getPath()); + return (LeafSchemaNode) schema; + } + + public SchemaNode startLeafSet(final NodeIdentifier name) { + final SchemaNode schema = getSchema(name); + + Preconditions.checkArgument(schema instanceof LeafListSchemaNode, "Node %s is not a leaf-list", schema.getPath()); + schemaStack.push(schema); + return schema; + } + + public LeafListSchemaNode leafSetEntryNode() { + final Object parent = getParent(); + + Preconditions.checkArgument(parent instanceof LeafListSchemaNode, "Not currently in a leaf-list"); + return (LeafListSchemaNode) parent; + } + + public SchemaNode startChoiceNode(final NodeIdentifier name) { + LOG.debug("Enter choice {}", name); + final SchemaNode schema = getSchema(name); + + Preconditions.checkArgument(schema instanceof ChoiceNode, "Node %s is not a choice", schema.getPath()); + schemaStack.push(schema); + return schema; + } + + public SchemaNode startContainerNode(final NodeIdentifier name) { + LOG.debug("Enter container {}", name); + final SchemaNode schema = getSchema(name); + + Preconditions.checkArgument(schema instanceof ContainerSchemaNode, "Node %s is not a container", schema.getPath()); + schemaStack.push(schema); + return schema; + } + + public AugmentationSchema startAugmentationNode(final AugmentationIdentifier identifier) { + LOG.debug("Enter augmentation {}", identifier); + final Object parent = getParent(); + + Preconditions.checkArgument(parent instanceof AugmentationTarget, "Augmentation not allowed under %s", parent); + Preconditions.checkArgument(parent instanceof DataNodeContainer, "Augmentation allowed only in DataNodeContainer",parent); + final AugmentationSchema schema = SchemaUtils.findSchemaForAugment((AugmentationTarget) parent, identifier.getPossibleChildNames()); + HashSet realChildSchemas = new HashSet<>(); + for(DataSchemaNode child : schema.getChildNodes()) { + realChildSchemas.add(((DataNodeContainer) parent).getDataChildByName(child.getQName())); + } + AugmentationSchema resolvedSchema = new AugmentationSchemaProxy(schema, realChildSchemas); + schemaStack.push(resolvedSchema); + return resolvedSchema; + } + + public AnyXmlSchemaNode anyxmlNode(final NodeIdentifier name) { + final SchemaNode schema = getSchema(name); + + Preconditions.checkArgument(schema instanceof AnyXmlSchemaNode, "Node %s is not anyxml", schema.getPath()); + return (AnyXmlSchemaNode)schema; + } + + public Object endNode() { + return schemaStack.pop(); + } +} diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java index 2628df2419..e735b28d61 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/TypeDefinitionAwareCodec.java @@ -25,15 +25,12 @@ import com.google.common.base.Strings; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.io.BaseEncoding; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.xml.bind.DatatypeConverter; - import org.opendaylight.yangtools.yang.data.api.codec.BinaryCodec; import org.opendaylight.yangtools.yang.data.api.codec.BitsCodec; import org.opendaylight.yangtools.yang.data.api.codec.BooleanCodec; @@ -97,11 +94,11 @@ public abstract class TypeDefinitionAwareCodec> i return 8; } else { String formatedMessage = String.format("Incorrect lexical representation of integer value: %s." - + "\nAn integer value can be defined as: " - + "\n - a decimal number," - + "\n - a hexadecimal number (prefix 0x)," - + "\n - an octal number (prefix 0)." - + "\nSigned values are allowed. Spaces between digits are NOT allowed.", integer); + + "%nAn integer value can be defined as: " + + "%n - a decimal number," + + "%n - a hexadecimal number (prefix 0x)," + + "%n - an octal number (prefix 0)." + + "%nSigned values are allowed. Spaces between digits are NOT allowed.", integer); throw new NumberFormatException(formatedMessage); } } @@ -344,12 +341,12 @@ public abstract class TypeDefinitionAwareCodec> i @Override public String deserialize(final String stringRepresentation) { - return stringRepresentation == null ? "" :stringRepresentation; + return stringRepresentation == null ? "" : stringRepresentation; } @Override public String serialize(final String data) { - return data == null ? "" : data.toString(); + return data == null ? "" : data; } }; diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XMLStreamNormalizedNodeStreamWriter.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XMLStreamNormalizedNodeStreamWriter.java new file mode 100644 index 0000000000..c244867077 --- /dev/null +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XMLStreamNormalizedNodeStreamWriter.java @@ -0,0 +1,224 @@ +/* + * 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.yangtools.yang.data.impl.codec.xml; + +import com.google.common.base.Preconditions; + +import java.io.IOException; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.Node; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.codec.SchemaTracker; +import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; + +/** + * A {@link NormalizedNodeStreamWriter} which translates the events into an + * {@link XMLStreamWriter}, resulting in a RFC 6020 XML encoding. + */ +public final class XMLStreamNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter { + private static final XmlStreamUtils UTILS = XmlStreamUtils.create(XmlUtils.DEFAULT_XML_CODEC_PROVIDER); + + private final XMLStreamWriter writer; + private final SchemaTracker tracker; + + private XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) { + this.writer = Preconditions.checkNotNull(writer); + this.tracker = SchemaTracker.create(context, path); + } + + /** + * Create a new writer with the specified context as its root. + * + * @param writer Output {@link XMLStreamWriter} + * @param context Associated {@link SchemaContext}. + * @return A new {@link NormalizedNodeStreamWriter} + */ + public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context) { + return create( writer, context, SchemaPath.ROOT); + } + + /** + * Create a new writer with the specified context and rooted in the specified schema path + * + * @param writer Output {@link XMLStreamWriter} + * @param context Associated {@link SchemaContext}. + * + * @return A new {@link NormalizedNodeStreamWriter} + */ + public static NormalizedNodeStreamWriter create(final XMLStreamWriter writer, final SchemaContext context, final SchemaPath path) { + final Boolean repairing = (Boolean) writer.getProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES); + Preconditions.checkArgument(repairing == true, "XML Stream Writer has to be repairing namespaces"); + return new XMLStreamNormalizedNodeStreamWriter(writer, context, path); + } + + private void writeElement(final QName qname, final TypeDefinition type, final Object value) throws IOException { + final String ns = qname.getNamespace().toString(); + + try { + if (value != null) { + writer.writeStartElement(ns, qname.getLocalName()); + UTILS.writeValue(writer, type, value); + writer.writeEndElement(); + } else { + writer.writeEmptyElement(ns, qname.getLocalName()); + } + } catch (XMLStreamException e) { + throw new IOException("Failed to emit element", e); + } + } + + private void startElement(final QName qname) throws IOException { + try { + writer.writeStartElement(qname.getNamespace().toString(), qname.getLocalName()); + } catch (XMLStreamException e) { + throw new IOException("Failed to start element", e); + } + } + + private void startList(final NodeIdentifier name) { + tracker.startList(name); + } + + private void startListItem(final PathArgument name) throws IOException { + tracker.startListItem(name); + startElement(name.getNodeType()); + } + + @Override + public void leafNode(final NodeIdentifier name, final Object value) throws IOException { + final LeafSchemaNode schema = tracker.leafNode(name); + + writeElement(schema.getQName(), schema.getType(), value); + } + + @Override + public void startLeafSet(final NodeIdentifier name, final int childSizeHint) { + tracker.startLeafSet(name); + } + + @Override + public void leafSetEntryNode(final Object value) throws IOException { + final LeafListSchemaNode schema = tracker.leafSetEntryNode(); + writeElement(schema.getQName(), schema.getType(), value); + } + + @Override + public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + final SchemaNode schema = tracker.startContainerNode(name); + startElement(schema.getQName()); + } + + @Override + public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) { + startList(name); + } + + @Override + public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException { + startListItem(name); + } + + @Override + public void startMapNode(final NodeIdentifier name, final int childSizeHint) { + startList(name); + } + + @Override + public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint) throws IOException { + startListItem(identifier); + } + + @Override + public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) { + startList(name); + } + + @Override + public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) { + tracker.startChoiceNode(name); + } + + @Override + public void startAugmentationNode(final AugmentationIdentifier identifier) { + tracker.startAugmentationNode(identifier); + } + + @Override + public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException { + final AnyXmlSchemaNode schema = tracker.anyxmlNode(name); + final QName qname = schema.getQName(); + final String ns = qname.getNamespace().toString(); + + try { + if (value != null) { + writer.writeStartElement(ns, qname.getLocalName()); + UTILS.writeValue(writer, (Node)value, schema); + writer.writeEndElement(); + } else { + writer.writeEmptyElement(ns, qname.getLocalName()); + } + } catch (XMLStreamException e) { + throw new IOException("Failed to emit element", e); + } + } + + @Override + public void endNode() throws IOException { + final Object schema = tracker.endNode(); + + try { + if (schema instanceof ListSchemaNode) { + // For lists, we only emit end element on the inner frame + final Object parent = tracker.getParent(); + if (parent == schema) { + writer.writeEndElement(); + } + } else if (schema instanceof ContainerSchemaNode) { + // Emit container end element + writer.writeEndElement(); + } + } catch (XMLStreamException e) { + throw new IOException("Failed to end element", e); + } + } + + @Override + public void close() throws IOException { + try { + writer.close(); + } catch (XMLStreamException e) { + throw new IOException("Failed to close writer", e); + } + } + + @Override + public void flush() throws IOException { + try { + writer.flush(); + } catch (XMLStreamException e) { + throw new IOException("Failed to flush writer", e); + } + } +} diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlUtils.java index e097771e84..7312c36382 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlUtils.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/XmlUtils.java @@ -8,9 +8,7 @@ package org.opendaylight.yangtools.yang.data.impl.codec.xml; import java.util.Map; - import javax.annotation.Nonnull; - import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; @@ -50,10 +48,10 @@ public final class XmlUtils { if (pathArgument instanceof NodeIdentifierWithPredicates) { Map predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues(); - for (QName keyValue : predicates.keySet()) { - String predicateValue = String.valueOf(predicates.get(keyValue)); + for (Map.Entry entry : predicates.entrySet()) { + String predicateValue = String.valueOf(entry.getValue()); textContent.append('['); - textContent.append(prefixes.encodeQName(keyValue)); + textContent.append(prefixes.encodeQName(entry.getKey())); textContent.append("='"); textContent.append(predicateValue); textContent.append("']"); diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java index fc5c20e6fd..73361f1922 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/ImmutableNodes.java @@ -27,11 +27,11 @@ public final class ImmutableNodes { throw new UnsupportedOperationException("Utilities class should not be instantiated"); } - public static final CollectionNodeBuilder mapNodeBuilder() { + public static CollectionNodeBuilder mapNodeBuilder() { return ImmutableMapNodeBuilder.create(); } - public static final CollectionNodeBuilder mapNodeBuilder(final QName name) { + public static CollectionNodeBuilder mapNodeBuilder(final QName name) { return ImmutableMapNodeBuilder.create().withNodeIdentifier(new NodeIdentifier(name)); } @@ -42,7 +42,7 @@ public final class ImmutableNodes { * @param value Value of leaf node * @return Leaf node with supplied identifier and value */ - public static final LeafNode leafNode(final NodeIdentifier name,final T value) { + public static LeafNode leafNode(final NodeIdentifier name,final T value) { return ImmutableLeafNodeBuilder.create() .withNodeIdentifier(name) .withValue(value) @@ -50,14 +50,13 @@ public final class ImmutableNodes { } /** - * * Construct immutable leaf node * * @param name QName which will be used as node identifier * @param value Value of leaf node. * @return Leaf node with supplied identifier and value */ - public static final LeafNode leafNode(final QName name,final T value) { + public static LeafNode leafNode(final QName name,final T value) { return leafNode(new NodeIdentifier(name), value); } diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java index 493c6ceec0..7a6627eb5e 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/builder/impl/valid/DataValidationException.java @@ -9,7 +9,6 @@ package org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.valid; import java.util.Map; import java.util.Set; - import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; @@ -26,26 +25,26 @@ public class DataValidationException extends RuntimeException { public static void checkLegalChild(final boolean isLegal, final YangInstanceIdentifier.PathArgument child, final DataNodeContainer schema, final Set childNodes, final Set augments) { - if (isLegal == false) { + if (!isLegal) { throw new IllegalChildException(child, schema, childNodes, augments); } } public static void checkLegalChild(final boolean isLegal, final YangInstanceIdentifier.PathArgument child, final DataSchemaNode schema, final Set childNodes) { - if (isLegal == false) { + if (!isLegal) { throw new IllegalChildException(child, schema, childNodes); } } public static void checkLegalChild(final boolean isLegal, final YangInstanceIdentifier.PathArgument child, final ChoiceNode schema) { - if (isLegal == false) { + if (!isLegal) { throw new IllegalChildException(child, schema); } } public static void checkLegalData(final boolean isLegal, final String messageTemplate, final Object... messageAttrs) { - if (isLegal == false) { + if (!isLegal) { throw new DataValidationException(String.format(messageTemplate, messageAttrs)); } } @@ -56,9 +55,6 @@ public class DataValidationException extends RuntimeException { Object expectedValue = nodeId.getKeyValues().get(keyQName); Object actualValue = childNode.getValue(); - if (childNode == null) { - throw new IllegalListKeyException(keyQName, nodeId, actualValue, expectedValue); - } } public static void checkListKey(final DataContainerChild childNode, final QName keyQName, final YangInstanceIdentifier.NodeIdentifierWithPredicates nodeId) { diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java index 486c1c2cf5..6c24d4f06c 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeFactory.java @@ -24,7 +24,7 @@ public final class InMemoryDataTreeFactory implements DataTreeFactory { final NodeIdentifier root = new NodeIdentifier(SchemaContext.NAME); final NormalizedNode data = Builders.containerBuilder().withNodeIdentifier(root).build(); - return new InMemoryDataTree(TreeNodeFactory.createTreeNode(data, Version.initial()), null); + return new InMemoryDataTree(TreeNodeFactory.createTreeNodeRecursively(data, Version.initial()), null); } /** diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java index e26c32ee5b..de4bcef796 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/InMemoryDataTreeModification.java @@ -39,7 +39,7 @@ final class InMemoryDataTreeModification implements DataTreeModification { InMemoryDataTreeModification(final InMemoryDataTreeSnapshot snapshot, final RootModificationApplyOperation resolver) { this.snapshot = Preconditions.checkNotNull(snapshot); this.strategyTree = Preconditions.checkNotNull(resolver).snapshot(); - this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode()); + this.rootNode = ModifiedNode.createUnmodified(snapshot.getRootNode(), false); /* * We could allocate version beforehand, since Version contract * states two allocated version must be allways different. @@ -141,8 +141,13 @@ final class InMemoryDataTreeModification implements DataTreeModification { ModifiedNode modification = rootNode; // We ensure strategy is present. ModificationApplyOperation operation = resolveModificationStrategy(path); + boolean isOrdered = true; + if (operation instanceof SchemaAwareApplyOperation) { + isOrdered = ((SchemaAwareApplyOperation) operation).isOrdered(); + } + for (PathArgument pathArg : path.getPathArguments()) { - modification = modification.modifyChild(pathArg); + modification = modification.modifyChild(pathArg, isOrdered); } return OperationWithModification.from(operation, modification); } diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java index 56353350af..27b42cc8fe 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModifiedNode.java @@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree; import com.google.common.base.Optional; import com.google.common.base.Predicate; +import java.util.HashMap; import org.opendaylight.yangtools.concepts.Identifiable; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; @@ -48,16 +49,22 @@ final class ModifiedNode implements StoreTreeNode, Identifiable children = new LinkedHashMap<>(); + private final Map children; private final Optional original; private final PathArgument identifier; private ModificationType modificationType = ModificationType.UNMODIFIED; private Optional snapshotCache; private NormalizedNode value; - private ModifiedNode(final PathArgument identifier, final Optional original) { + private ModifiedNode(final PathArgument identifier, final Optional original, boolean isOrdered) { this.identifier = identifier; this.original = original; + + if (isOrdered) { + children = new LinkedHashMap<>(); + } else { + children = new HashMap<>(); + } } /** @@ -119,7 +126,7 @@ final class ModifiedNode implements StoreTreeNode, Identifiable, Identifiable, Identifiable modifications) { @@ -223,6 +233,11 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp entryStrategy = Optional. of(new ValueNodeModificationStrategy.LeafSetEntryModificationStrategy(schema)); } + @Override + boolean isOrdered() { + return true; + } + @SuppressWarnings("rawtypes") @Override protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode original) { @@ -248,6 +263,11 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp entryStrategy = Optional. of(new DataNodeContainerModificationStrategy.ListEntryModificationStrategy(schema)); } + @Override + boolean isOrdered() { + return true; + } + @SuppressWarnings("rawtypes") @Override protected NormalizedNodeContainerBuilder createBuilder(final NormalizedNode original) { diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java index 511fc322cb..e1df47c7d4 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/OperationWithModification.java @@ -61,8 +61,13 @@ final class OperationWithModification { } public OperationWithModification forChild(final PathArgument childId) { - ModifiedNode childMod = modification.modifyChild(childId); - Optional childOp = applyOperation.getChild(childId); - return from(childOp.get(),childMod); + ModificationApplyOperation childOp = applyOperation.getChild(childId).get(); + boolean isOrdered = true; + if (childOp instanceof SchemaAwareApplyOperation) { + isOrdered = ((SchemaAwareApplyOperation) childOp).isOrdered(); + } + ModifiedNode childMod = modification.modifyChild(childId, isOrdered); + + return from(childOp,childMod); } } diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java index d0a0bcd33f..d7aa826c23 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java @@ -9,19 +9,24 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree; import com.google.common.base.Optional; import com.google.common.base.Preconditions; +import com.google.common.collect.Iterables; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException; import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException; import org.opendaylight.yangtools.yang.data.api.schema.tree.IncorrectDataStructureException; import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType; +import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.MutableTreeNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNode; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.TreeNodeFactory; import org.opendaylight.yangtools.yang.data.api.schema.tree.spi.Version; +import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder; +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.ChoiceNode; @@ -158,7 +163,18 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation { } } - protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification, final Optional current) throws DataValidationFailedException { + /** + * Checks if write operation can be applied to current TreeNode. + * The operation checks if original tree node to which the modification is going to be applied exists and if + * current node in TreeNode structure exists. + * + * @param path Path from current node in TreeNode + * @param modification modification to apply + * @param current current node in TreeNode for modification to apply + * @throws DataValidationFailedException + */ + protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification, + final Optional current) throws DataValidationFailedException { Optional original = modification.getOriginal(); if (original.isPresent() && current.isPresent()) { checkNotConflicting(path, original.get(), current.get()); @@ -176,6 +192,10 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation { } } + boolean isOrdered() { + return false; + } + @Override public final Optional apply(final ModifiedNode modification, final Optional currentMeta, final Version version) { @@ -234,6 +254,11 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation { entryStrategy = Optional. of(new DataNodeContainerModificationStrategy.UnkeyedListItemModificationStrategy(schema)); } + @Override + boolean isOrdered() { + return true; + } + @Override protected TreeNode applyMerge(final ModifiedNode modification, final TreeNode currentMeta, final Version version) { @@ -249,12 +274,65 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation { @Override protected TreeNode applyWrite(final ModifiedNode modification, final Optional currentMeta, final Version version) { + final NormalizedNode newValue = modification.getWrittenValue(); + final TreeNode newValueMeta = TreeNodeFactory.createTreeNode(newValue, version); + + if (Iterables.isEmpty(modification.getChildren())) { + return newValueMeta; + } + /* - * FIXME: BUG-1258: This is inefficient: it needlessly creates index nodes for the entire subtree. - * We can determine the depth into which metadata need to be created from the modification - * -- if it does not have children, no need to bother with metadata. + * This is where things get interesting. The user has performed a write and + * then she applied some more modifications to it. So we need to make sense + * of that an apply the operations on top of the written value. We could have + * done it during the write, but this operation is potentially expensive, so + * we have left it out of the fast path. + * + * As it turns out, once we materialize the written data, we can share the + * code path with the subtree change. So let's create an unsealed TreeNode + * and run the common parts on it -- which end with the node being sealed. */ - return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), version); + final MutableTreeNode mutable = newValueMeta.mutable(); + mutable.setSubtreeVersion(version); + + @SuppressWarnings("rawtypes") + final NormalizedNodeContainerBuilder dataBuilder = ImmutableUnkeyedListEntryNodeBuilder + .create((UnkeyedListEntryNode) newValue); + + return mutateChildren(mutable, dataBuilder, version, modification.getChildren()); + } + + /** + * Applies write/remove diff operation for each modification child in modification subtree. + * Operation also sets the Data tree references for each Tree Node (Index Node) in meta (MutableTreeNode) structure. + * + * @param meta MutableTreeNode (IndexTreeNode) + * @param data DataBuilder + * @param nodeVersion Version of TreeNode + * @param modifications modification operations to apply + * @return Sealed immutable copy of TreeNode structure with all Data Node references set. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + private TreeNode mutateChildren(final MutableTreeNode meta, final NormalizedNodeContainerBuilder data, + final Version nodeVersion, final Iterable modifications) { + + for (ModifiedNode mod : modifications) { + final PathArgument id = mod.getIdentifier(); + final Optional cm = meta.getChild(id); + + Optional result = resolveChildOperation(id).apply(mod, cm, nodeVersion); + if (result.isPresent()) { + final TreeNode tn = result.get(); + meta.addChild(tn); + data.addChild(tn.getData()); + } else { + meta.removeChild(id); + data.removeChild(id); + } + } + + meta.setData(data.build()); + return meta.seal(); } @Override diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java index 6979a64f71..3df0c80e95 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ValueNodeModificationStrategy.java @@ -62,7 +62,7 @@ abstract class ValueNodeModificationStrategy extends S @Override protected TreeNode applyWrite(final ModifiedNode modification, final Optional currentMeta, final Version version) { - return TreeNodeFactory.createTreeNode(modification.getWrittenValue(), version); + return TreeNodeFactory.createTreeNodeRecursively(modification.getWrittenValue(), version); } @Override diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java index a85acfd1e4..2896675c15 100644 --- a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ModificationMetadataTreeTest.java @@ -146,7 +146,7 @@ public class ModificationMetadataTreeTest { @Test public void basicReadWrites() { DataTreeModification modificationTree = new InMemoryDataTreeModification(new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper), + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper), rootOper); Optional> originalBarNode = modificationTree.readNode(OUTER_LIST_2_PATH); assertTrue(originalBarNode.isPresent()); diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TreeNodeUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TreeNodeUtilsTest.java index bc1f6cef4f..509037fbcc 100644 --- a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TreeNodeUtilsTest.java +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/TreeNodeUtilsTest.java @@ -94,7 +94,7 @@ public class TreeNodeUtilsTest { @Test public void findNodeTestNodeFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); Optional node = TreeNodeUtils.findNode(rootNode, OUTER_LIST_1_PATH); assertPresentAndType(node, TreeNode.class); @@ -103,7 +103,7 @@ public class TreeNodeUtilsTest { @Test public void findNodeTestNodeNotFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH) .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) // @@ -115,7 +115,7 @@ public class TreeNodeUtilsTest { @Test public void findNodeCheckedTestNodeFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); TreeNode foundNode = null; try { @@ -129,7 +129,7 @@ public class TreeNodeUtilsTest { @Test public void findNodeCheckedTestNodeNotFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH) .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) // @@ -145,7 +145,7 @@ public class TreeNodeUtilsTest { @Test public void findClosestOrFirstMatchTestNodeExists() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); Optional expectedNode = TreeNodeUtils.findNode(rootNode, TWO_TWO_PATH); assertPresentAndType(expectedNode, TreeNode.class); @@ -156,7 +156,7 @@ public class TreeNodeUtilsTest { @Test public void findClosestOrFirstMatchTestNodeDoesNotExist() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); final YangInstanceIdentifier outerListInnerListPath = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH) .node(TestModel.INNER_LIST_QNAME) @@ -174,7 +174,7 @@ public class TreeNodeUtilsTest { @Test public void getChildTestChildFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); Optional node = TreeNodeUtils.getChild(Optional.fromNullable(rootNode), TestModel.TEST_PATH.getLastPathArgument()); @@ -184,7 +184,7 @@ public class TreeNodeUtilsTest { @Test public void getChildTestChildNotFound() { InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext, - TreeNodeFactory.createTreeNode(createDocumentOne(), Version.initial()), rootOper); + TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper); TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode(); Optional node = TreeNodeUtils.getChild(Optional.fromNullable(rootNode), TestModel.OUTER_LIST_PATH.getLastPathArgument()); diff --git a/yang/yang-data-operations/src/main/java/org/opendaylight/yangtools/yang/data/operations/ChoiceNodeModification.java b/yang/yang-data-operations/src/main/java/org/opendaylight/yangtools/yang/data/operations/ChoiceNodeModification.java index e281ea7339..fdaf0c8f70 100644 --- a/yang/yang-data-operations/src/main/java/org/opendaylight/yangtools/yang/data/operations/ChoiceNodeModification.java +++ b/yang/yang-data-operations/src/main/java/org/opendaylight/yangtools/yang/data/operations/ChoiceNodeModification.java @@ -11,7 +11,6 @@ import java.util.Set; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; import org.opendaylight.yangtools.yang.data.impl.schema.Builders; import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder; @@ -72,7 +71,7 @@ final class ChoiceNodeModification extends Set childrenToProcessFiltered = Sets.newLinkedHashSet(); for (YangInstanceIdentifier.PathArgument childToProcess : childrenToProcess) { // child from other cases, skip - if (childToProcess instanceof AugmentationNode + if (childToProcess instanceof YangInstanceIdentifier.AugmentationIdentifier && SchemaUtils.belongsToCaseAugment(detectedCase, (YangInstanceIdentifier.AugmentationIdentifier) childToProcess) == false) { continue; diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java new file mode 100644 index 0000000000..dc04682699 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringIdentityrefCodec.java @@ -0,0 +1,34 @@ +/* + * 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.yangtools.yang.data.util; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.Module; + +@Beta +public abstract class AbstractModuleStringIdentityrefCodec extends AbstractStringIdentityrefCodec { + /** + * Resolve a string prefix into the corresponding module. + * + * @param prefix + * @return module mapped to prefix, or null if the module cannot be resolved + */ + protected abstract Module moduleForPrefix(@Nonnull String prefix); + + @Override + protected final QName createQName(final String prefix, final String localName) { + final Module module = moduleForPrefix(prefix); + Preconditions.checkArgument(module != null, "Failed to lookup prefix %s", prefix); + return QName.create(module.getQNameModule(), localName); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java new file mode 100644 index 0000000000..7e618410f8 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractModuleStringInstanceIdentifierCodec.java @@ -0,0 +1,39 @@ +/* + * 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.yangtools.yang.data.util; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; + +import javax.annotation.Nonnull; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.Module; + +/** + * Extension of {@link AbstractStringInstanceIdentifierCodec}, which instantiates + * QNames by first resolving the namespace and then looking the target namespace + * in the list of currently-subscribed modules. + */ +@Beta +public abstract class AbstractModuleStringInstanceIdentifierCodec extends AbstractStringInstanceIdentifierCodec { + /** + * Resolve a string prefix into the corresponding module. + * + * @param prefix + * @return module mapped to prefix, or null if the module cannot be resolved + */ + protected abstract Module moduleForPrefix(@Nonnull String prefix); + + @Override + protected final QName createQName(final String prefix, final String localName) { + final Module module = moduleForPrefix(prefix); + Preconditions.checkArgument(module != null, "Failed to lookup prefix %s", prefix); + return QName.create(module.getQNameModule(), localName); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java new file mode 100644 index 0000000000..620f46e204 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractNamespaceCodec.java @@ -0,0 +1,82 @@ +/* + * 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.yangtools.yang.data.util; + +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; + +import java.net.URI; +import java.util.Iterator; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import org.opendaylight.yangtools.yang.common.QName; + +abstract class AbstractNamespaceCodec { + private static final Splitter COLON_SPLITTER = Splitter.on(':'); + + /** + * Return string prefix for a particular namespace, allocating a new one if necessary. + * + * @param namespace Namespace to map + * @return Allocated unique prefix, or null if the prefix cannot be mapped. + */ + protected abstract @Nullable String prefixForNamespace(final @Nonnull URI namespace); + + /** + * Create a QName for a prefix and local name. + * + * @param prefix Prefix for namespace + * @param localName local name + * @return QName + * @throws IllegalArgumentException if the prefix cannot be resolved + */ + protected abstract @Nullable QName createQName(final @Nonnull String prefix, final @Nonnull String localName); + + private static String getIdAndPrefixAsStr(final String pathPart) { + int predicateStartIndex = pathPart.indexOf('['); + return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex); + } + + protected final StringBuilder appendQName(final StringBuilder sb, final QName qname) { + final String prefix = prefixForNamespace(qname.getNamespace()); + Preconditions.checkArgument(prefix != null, "Failed to map QName {}", qname); + sb.append(prefix); + sb.append(':'); + sb.append(qname.getLocalName()); + return sb; + } + + protected final QName parseQName(final String str) { + final String xPathPartTrimmed = getIdAndPrefixAsStr(str).trim(); + final Iterator it = COLON_SPLITTER.split(xPathPartTrimmed).iterator(); + + // Empty string + if (!it.hasNext()) { + return null; + } + + final String prefix = it.next().trim(); + if (prefix.isEmpty()) { + return null; + } + + // it is not "prefix:value" + if (!it.hasNext()) { + return null; + } + + final String identifier = it.next().trim(); + if (identifier.isEmpty()) { + return null; + } + + return createQName(prefix, identifier); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java new file mode 100644 index 0000000000..6e150676e0 --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringIdentityrefCodec.java @@ -0,0 +1,30 @@ +/* + * 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.yangtools.yang.data.util; + +import com.google.common.annotations.Beta; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec; + +/** + * Abstract utility class for representations which encode Identityref as a + * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name). + */ +@Beta +public abstract class AbstractStringIdentityrefCodec extends AbstractNamespaceCodec implements IdentityrefCodec { + @Override + public String serialize(final QName data) { + return appendQName(new StringBuilder(), data).toString(); + } + + @Override + public QName deserialize(final String data) { + return parseQName(data); + } +} diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java new file mode 100644 index 0000000000..5140f2727d --- /dev/null +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/AbstractStringInstanceIdentifierCodec.java @@ -0,0 +1,145 @@ +/* + * 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.yangtools.yang.data.util; + +import com.google.common.annotations.Beta; +import com.google.common.base.Preconditions; +import com.google.common.base.Splitter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.codec.InstanceIdentifierCodec; + +/** + * Abstract utility class for representations which encode {@link YangInstanceIdentifier} as a + * prefix:name tuple. Typical uses are RESTCONF/JSON (module:name) and XML (prefix:name). + */ +@Beta +public abstract class AbstractStringInstanceIdentifierCodec extends AbstractNamespaceCodec implements InstanceIdentifierCodec { + private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]"); + private static final Splitter SLASH_SPLITTER = Splitter.on('/'); + + @Override + public final String serialize(final YangInstanceIdentifier data) { + StringBuilder sb = new StringBuilder(); + for (PathArgument arg : data.getPathArguments()) { + sb.append('/'); + appendQName(sb, arg.getNodeType()); + + if (arg instanceof NodeIdentifierWithPredicates) { + for (Map.Entry entry : ((NodeIdentifierWithPredicates) arg).getKeyValues().entrySet()) { + sb.append('['); + appendQName(sb, entry.getKey()); + sb.append("='"); + sb.append(String.valueOf(entry.getValue())); + sb.append("']"); + } + } else if (arg instanceof NodeWithValue) { + sb.append("[.='"); + sb.append(((NodeWithValue) arg).getValue()); + sb.append("']"); + } + } + + return sb.toString(); + } + + @Override + public final YangInstanceIdentifier deserialize(final String data) { + Preconditions.checkNotNull(data, "Data may not be null"); + + final Iterator xPathParts = SLASH_SPLITTER.split(data).iterator(); + + // must be at least "/pr:node" + if (!xPathParts.hasNext() || !xPathParts.next().isEmpty() || !xPathParts.hasNext()) { + return null; + } + + List result = new ArrayList<>(); + while (xPathParts.hasNext()) { + String xPathPartTrimmed = xPathParts.next().trim(); + + PathArgument pathArgument = toPathArgument(xPathPartTrimmed); + if (pathArgument != null) { + result.add(pathArgument); + } + } + return YangInstanceIdentifier.create(result); + } + + private PathArgument toPathArgument(final String xPathArgument) { + final QName mainQName = parseQName(xPathArgument); + + // predicates + final Matcher matcher = PREDICATE_PATTERN.matcher(xPathArgument); + final Map predicates = new HashMap<>(); + QName currentQName = mainQName; + + while (matcher.find()) { + final String predicateStr = matcher.group(1).trim(); + final int indexOfEqualityMark = predicateStr.indexOf('='); + if (indexOfEqualityMark != -1) { + final String predicateValue = toPredicateValue(predicateStr.substring(indexOfEqualityMark + 1)); + if (predicateValue == null) { + return null; + } + + if (predicateStr.charAt(0) != '.') { + // target is not a leaf-list + currentQName = parseQName(predicateStr.substring(0, indexOfEqualityMark)); + if (currentQName == null) { + return null; + } + } + predicates.put(currentQName, predicateValue); + } + } + + if (predicates.isEmpty()) { + return new YangInstanceIdentifier.NodeIdentifier(mainQName); + } else { + return new YangInstanceIdentifier.NodeIdentifierWithPredicates(mainQName, predicates); + } + } + + private static String toPredicateValue(final String predicatedValue) { + final String predicatedValueTrimmed = predicatedValue.trim(); + if (predicatedValue.isEmpty()) { + return null; + } + + switch (predicatedValueTrimmed.charAt(0)) { + case '"': + return trimIfEndIs(predicatedValueTrimmed, '"'); + case '\'': + return trimIfEndIs(predicatedValueTrimmed, '\''); + default: + return null; + } + } + + private static String trimIfEndIs(final String str, final char end) { + final int l = str.length() - 1; + if (str.charAt(l) != end) { + return null; + } + + return str.substring(1, l); + } +} diff --git a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java index 809353a4d7..d3de2502a1 100644 --- a/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java +++ b/yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java @@ -49,7 +49,7 @@ class YangToSourcesProcessor { private final MavenProject project; private final boolean inspectDependencies; private final BuildContext buildContext; - private YangProvider yangProvider; + private final YangProvider yangProvider; @VisibleForTesting YangToSourcesProcessor(Log log, File yangFilesRootDir, File[] excludedFiles, List codeGenerators, @@ -180,13 +180,27 @@ class YangToSourcesProcessor { static class YangProvider { - - void addYangsToMetaInf(Log log, MavenProject project, File yangFilesRootDir, File[] excludedFiles) throws MojoFailureException { // copy project's src/main/yang/*.yang to target/generated-sources/yang/META-INF/yang/*.yang + File generatedYangDir = new File(project.getBasedir(), CodeGeneratorArg.YANG_GENERATED_DIR); + addYangsToMetaInf(log, project, yangFilesRootDir, excludedFiles, generatedYangDir); + + // Also copy to the actual build output dir if different than "target". When running in + // Eclipse this can differ (eg "target-ide"). + + File actualGeneratedYangDir = new File(project.getBuild().getDirectory(), + CodeGeneratorArg.YANG_GENERATED_DIR.replace("target" + File.separator, "")); + if(!actualGeneratedYangDir.equals(generatedYangDir)) { + addYangsToMetaInf(log, project, yangFilesRootDir, excludedFiles, actualGeneratedYangDir); + } + } + + private void addYangsToMetaInf(Log log, MavenProject project, File yangFilesRootDir, + File[] excludedFiles, File generatedYangDir) + throws MojoFailureException { File withMetaInf = new File(generatedYangDir, META_INF_YANG_STRING); withMetaInf.mkdirs(); @@ -197,9 +211,8 @@ class YangToSourcesProcessor { org.apache.commons.io.FileUtils.copyFile(file, new File(withMetaInf, file.getName())); } } catch (IOException e) { - String message = "Unable to list yang files into resource folder"; - log.warn(message, e); - throw new MojoFailureException(message, e); + log.warn(String.format("Failed to generate files into root %s", yangFilesRootDir), e); + throw new MojoFailureException("Unable to list yang files into resource folder", e); } setResource(generatedYangDir, project); diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java index 7bae5f0f57..56d240aed1 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java @@ -92,14 +92,21 @@ public abstract class SchemaPath implements Immutable { * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()} * is invoked. */ - private ImmutableList legacyPath; + private volatile ImmutableList legacyPath; private ImmutableList getLegacyPath() { - if (legacyPath == null) { - legacyPath = ImmutableList.copyOf(getPathTowardsRoot()).reverse(); + ImmutableList ret = legacyPath; + if (ret == null) { + synchronized (this) { + ret = legacyPath; + if (ret == null) { + ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse(); + legacyPath = ret; + } + } } - return legacyPath; + return ret; } /** diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java index e8bcc06510..722ac918e0 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/repo/util/AbstractSchemaRepository.java @@ -146,7 +146,7 @@ public abstract class AbstractSchemaRepository implements SchemaRepository, Sche } if (m.isEmpty()) { - sources.remove(m); + sources.remove(source.getSourceIdentifier()); } } } diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/Uint16.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/Uint16.java index d557449220..5c233df5ad 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/Uint16.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/Uint16.java @@ -19,7 +19,7 @@ public final class Uint16 extends AbstractUnsignedInteger implements Immutable { public static final int MAX_VALUE = 65535; private static final String DESCRIPTION = "uint16 represents integer values between 0 and 65535, inclusively."; - private static Uint16 INSTANCE = new Uint16(); + private static final Uint16 INSTANCE = new Uint16(); private Uint16() { super(BaseTypes.UINT16_QNAME, DESCRIPTION, MAX_VALUE, ""); diff --git a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/UnionType.java b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/UnionType.java index 41e91351af..ec8edd3411 100644 --- a/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/UnionType.java +++ b/yang/yang-model-util/src/main/java/org/opendaylight/yangtools/yang/model/util/UnionType.java @@ -90,7 +90,7 @@ public final class UnionType implements UnionTypeDefinition { public int hashCode() { final int prime = 31; int result = 1; - result = prime * result + ((types == null) ? 0 : types.hashCode()); + result = prime * result + types.hashCode(); return result; } @@ -119,9 +119,9 @@ public final class UnionType implements UnionTypeDefinition { builder.append(BaseTypes.UNION_QNAME); builder.append(" (types=["); for (TypeDefinition td : types) { - builder.append(", " + td.getQName().getLocalName()); + builder.append(", " ).append(td.getQName().getLocalName()); } - builder.append("]"); + builder.append(']'); return builder.toString(); } diff --git a/yang/yang-parser-impl/src/main/antlr/YangParser.g4 b/yang/yang-parser-impl/src/main/antlr/YangParser.g4 index c1b004dfd2..3484b79fc3 100644 --- a/yang/yang-parser-impl/src/main/antlr/YangParser.g4 +++ b/yang/yang-parser-impl/src/main/antlr/YangParser.g4 @@ -34,6 +34,13 @@ unknown_statement : (YIN_ELEMENT_KEYWORD | YANG_VERSION_KEYWORD | WHEN_KEYWORD | ANYXML_KEYWORD | IDENTIFIER) string? (SEMICOLON | (LEFT_BRACE (unknown_statement | identifier_stmt)* RIGHT_BRACE)*); stmtend : (SEMICOLON) | (LEFT_BRACE identifier_stmt? RIGHT_BRACE); + +/* DO NOT replace stmtsep in rest of grammar with identifier_stmt!!! It might seems as code duplicity here, but this one is necessary. + Body of identifier_stmt generated from this grammar in YangParserListener is implemented in YangParserListenerImpl. + To ensure that all of the identifier_stmts will be resolved correctly the YangParserListenerImpl contains code that handles + specifcly identifier_stmts -> i.e. transforms identifier_stmt into QName. The stmtsep is used for parsing extension statements + placed outside of body_stmt. + */ stmtsep : IDENTIFIER string? (stmtend | (LEFT_BRACE unknown_statement* RIGHT_BRACE)); deviate_replace_stmt : DEVIATE_KEYWORD string /* REPLACE_KEYWORD */ (SEMICOLON | (LEFT_BRACE (identifier_stmt |type_stmt | units_stmt | default_stmt | config_stmt | mandatory_stmt | min_elements_stmt | max_elements_stmt )* RIGHT_BRACE)); deviate_delete_stmt : DEVIATE_KEYWORD string /* DELETE_KEYWORD */ (SEMICOLON | (LEFT_BRACE (identifier_stmt |units_stmt | must_stmt | unique_stmt | default_stmt )* RIGHT_BRACE)); @@ -133,9 +140,9 @@ yang_version_stmt : YANG_VERSION_KEYWORD string stmtend; data_def_stmt : container_stmt | leaf_stmt | leaf_list_stmt | list_stmt | choice_stmt | anyxml_stmt | uses_stmt; body_stmts : (( identifier_stmt| extension_stmt | feature_stmt | identity_stmt | typedef_stmt | grouping_stmt | data_def_stmt | augment_stmt | rpc_stmt | notification_stmt | deviation_stmt) )*; revision_stmts : (revision_stmt )* (stmtsep)*; -linkage_stmts : (import_stmt stmtsep? | include_stmt stmtsep?)*; -meta_stmts : (organization_stmt stmtsep? | contact_stmt stmtsep? | description_stmt stmtsep? | reference_stmt stmtsep?)*; -submodule_header_stmts : (yang_version_stmt stmtsep? | belongs_to_stmt stmtsep?)+ ; -module_header_stmts : (yang_version_stmt stmtsep? | namespace_stmt stmtsep? | prefix_stmt stmtsep?)+ ; +linkage_stmts : (import_stmt stmtsep* | include_stmt stmtsep*)*; +meta_stmts : (organization_stmt stmtsep* | contact_stmt stmtsep* | description_stmt stmtsep* | reference_stmt stmtsep*)*; +submodule_header_stmts : (yang_version_stmt stmtsep* | belongs_to_stmt stmtsep*)+ ; +module_header_stmts : (yang_version_stmt stmtsep* | namespace_stmt stmtsep* | prefix_stmt stmtsep*)+ ; submodule_stmt : SUBMODULE_KEYWORD string LEFT_BRACE submodule_header_stmts linkage_stmts meta_stmts revision_stmts body_stmts RIGHT_BRACE; -module_stmt : MODULE_KEYWORD string LEFT_BRACE stmtsep? module_header_stmts linkage_stmts meta_stmts revision_stmts body_stmts RIGHT_BRACE; \ No newline at end of file +module_stmt : MODULE_KEYWORD string LEFT_BRACE stmtsep* module_header_stmts linkage_stmts meta_stmts revision_stmts body_stmts RIGHT_BRACE; \ No newline at end of file diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ConstraintsBuilderImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ConstraintsBuilderImpl.java index 52f8acdb3c..11fcb347d4 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ConstraintsBuilderImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/ConstraintsBuilderImpl.java @@ -288,7 +288,7 @@ public final class ConstraintsBuilderImpl implements ConstraintsBuilder { } else if (!mustConstraints.equals(other.mustConstraints)) { return false; } - if (mandatory != other.mandatory) { + if (!mandatory.equals(other.mandatory)) { return false; } if (minElements == null) { diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/GroupingUtils.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/GroupingUtils.java index 6eb400c483..e1792c899f 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/GroupingUtils.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/GroupingUtils.java @@ -44,7 +44,9 @@ public final class GroupingUtils { * all loaded modules * @param module * current module - * @return grouping with given name if found, null otherwise + * @return grouping with given name, never null + * @throws YangParseException + * if no grouping found */ public static GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder, final Map> modules, final ModuleBuilder module) { diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/util/AbstractDocumentedDataNodeContainer.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/util/AbstractDocumentedDataNodeContainer.java index 30e28d46bd..766b61fbce 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/util/AbstractDocumentedDataNodeContainer.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/util/AbstractDocumentedDataNodeContainer.java @@ -26,6 +26,7 @@ public abstract class AbstractDocumentedDataNodeContainer extends AbstractDocume private final Set groupings; private final Set uses; private final Set> typeDefinitions; + private final Set publicChildNodes; protected AbstractDocumentedDataNodeContainer(final AbstractDocumentedDataNodeContainerBuilder data) { super(data); @@ -36,6 +37,7 @@ public abstract class AbstractDocumentedDataNodeContainer extends AbstractDocume groupings = ImmutableSet.copyOf(data.getGroupings()); uses = ImmutableSet.copyOf(data.getUsesNodes()); typeDefinitions = ImmutableSet.copyOf(data.getTypeDefinitions()); + publicChildNodes = ImmutableSet.copyOf(childNodes.values()); } @Override @@ -45,7 +47,7 @@ public abstract class AbstractDocumentedDataNodeContainer extends AbstractDocume @Override public final Set getChildNodes() { - return ImmutableSet.copyOf(childNodes.values()); + return publicChildNodes; } @Override diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java index 933ac6b6d4..83b56314cb 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/ParserListenerUtils.java @@ -11,17 +11,14 @@ import static com.google.common.base.Preconditions.checkState; import com.google.common.base.CharMatcher; import com.google.common.base.Optional; -import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.Lists; - import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; - import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; @@ -183,13 +180,13 @@ public final class ParserListenerUtils { continue; } /* - * + * * It is safe not to check last argument to be same * grammars enforces that. - * + * * FIXME: Introduce proper escaping and translation of escaped * characters here. - * + * */ sb.append(quoteMatcher.removeFrom(str.substring(1, str.length()-1))); } @@ -378,7 +375,15 @@ public final class ParserListenerUtils { ParseTree child = ctx.getChild(i); if (child instanceof Value_stmtContext) { String valueStr = stringFromNode(child); - value = Integer.valueOf(valueStr); + try { + // yang enum value has same restrictions as JAVA Integer + value = Integer.valueOf(valueStr); + } catch (NumberFormatException e) { + String err = String + .format("Error on enum '%s': the enum value MUST be in the range from -2147483648 to 2147483647, but was: %s", + name, valueStr); + throw new YangParseException(moduleName, ctx.getStart().getLine(), err, e); + } } else if (child instanceof Description_stmtContext) { description = stringFromNode(child); } else if (child instanceof Reference_stmtContext) { @@ -391,10 +396,6 @@ public final class ParserListenerUtils { if (value == null) { value = highestValue + 1; } - if (value < -2147483648 || value > 2147483647) { - throw new YangParseException(moduleName, ctx.getStart().getLine(), "Error on enum '" + name - + "': the enum value MUST be in the range from -2147483648 to 2147483647, but was: " + value); - } EnumPairImpl result = new EnumPairImpl(); result.qname = path.getPathTowardsRoot().iterator().next(); diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java index 38055d908d..7948158f4d 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserImpl.java @@ -240,7 +240,7 @@ public final class YangParserImpl implements YangContextParser { if (targetModule == null) { Module result = findModuleFromContext(context, module, prefix, 0); targetModule = new ModuleBuilder(result); - TreeMap map = modules.get(prefix); + TreeMap map = modules.get(targetModule.getNamespace()); if (map == null) { map = new TreeMap<>(); map.put(targetModule.getRevision(), targetModule); @@ -1061,10 +1061,6 @@ public final class YangParserImpl implements YangContextParser { ModuleBuilder module = BuilderUtils.getParentModule(usesNode); final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module); - if (targetGroupingBuilder == null) { - throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '" - + usesNode.getGroupingPath() + "' not found."); - } usesNode.setGrouping(targetGroupingBuilder); } } diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ModuleDependencySort.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ModuleDependencySort.java index f7e270ea1d..05dbd2e1b7 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ModuleDependencySort.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/ModuleDependencySort.java @@ -46,6 +46,9 @@ public final class ModuleDependencySort { private static final Function TOPOLOGY_FUNCTION = new Function() { @Override public Module apply(final TopologicalSort.Node input) { + if (input == null) { + return null; + } ModuleOrModuleBuilder moduleOrModuleBuilder = ((ModuleNodeImpl) input).getReference(); return moduleOrModuleBuilder.getModule(); } @@ -65,6 +68,9 @@ public final class ModuleDependencySort { @Override public ModuleBuilder apply(final TopologicalSort.Node input) { // Cast to ModuleBuilder from Node and return + if (input == null) { + return null; + } ModuleOrModuleBuilder moduleOrModuleBuilder = ((ModuleNodeImpl) input).getReference(); return moduleOrModuleBuilder.getModuleBuilder(); } @@ -95,6 +101,9 @@ public final class ModuleDependencySort { @Override public ModuleBuilder apply(final TopologicalSort.Node input) { + if (input == null) { + return null; + } ModuleOrModuleBuilder moduleOrModuleBuilder = ((ModuleNodeImpl) input).getReference(); if (moduleOrModuleBuilder.isModuleBuilder()) { return moduleOrModuleBuilder.getModuleBuilder(); diff --git a/yang/yang-parser-impl/src/test/resources/extensions/ext-use.yang b/yang/yang-parser-impl/src/test/resources/extensions/ext-use.yang index f36fc15b07..01c1dd9724 100644 --- a/yang/yang-parser-impl/src/test/resources/extensions/ext-use.yang +++ b/yang/yang-parser-impl/src/test/resources/extensions/ext-use.yang @@ -1,7 +1,19 @@ module ext-use { + ext:id "http://opendaylight.org"; yang-version 1; + ext:id "http://opendaylight.org"; + + ext:id2 "73354"; + ext:name "test-name"; + namespace "urn:simple.extension.use"; + ext:id "http://opendaylight.org"; + + ext:name "test-name"; + prefix "ext-use"; + ext:id "http://opendaylight.org"; + ext:name "test-name"; import ext-typedef { prefix "ext"; }