Bug 3670: Replaced YANG parser for new statement parser 26/25526/9
authorPeter Kajsa <pkajsa@cisco.com>
Mon, 21 Sep 2015 11:05:48 +0000 (13:05 +0200)
committerTony Tkacik <ttkacik@cisco.com>
Tue, 29 Sep 2015 16:56:07 +0000 (18:56 +0200)
Parser switch in yangtools:
- yang-maven-plugin switch to new parser
- repo and related classes e.g. SharedSchemaContextFactory, YangModelDependencyInfo, TextToASTTransformer
- parser switch in yangtools benchmarks

Yangtools retest:
- yang-maven-plugin-it test fix
- model-export SimpleModuleTest bug fix
- yang data unit tests fix
- yang-maven-plugin yang models from dependencies resolution fix
- additional unit tests switch to new statement parser
- extension definition fix in test models

Change-Id: I75c8b67af01212a8ac5b832625f9405bb0108455
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
57 files changed:
benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/BenchmarkModel.java
benchmarks/src/main/java/org/opendaylight/yangtools/yang/data/impl/tree/InMemoryDataTreeBenchmark.java
integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java [new file with mode: 0644]
yang-validation-tool/pom.xml
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Main.java [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Params.java [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/ParamsUtil.java [new file with mode: 0644]
yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/RetestUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/JsonStreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/NormalizedNodeToJsonStreamTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/RetestUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/StreamToNormalizedNodeTest.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestUtils.java [new file with mode: 0644]
yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestingNormalizedNodeStructuresCreator.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefix.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixInstanceIdentifierSerializer.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/RetestUtils.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/Bug2964Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlDocumentUtilsTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtilsTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/DataTreeCandidateValidatorTest2.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/DataTreeCandidateValidatorTest3.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest2.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest3.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTreeBuilderTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/InstanceIdToNodesTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/NormalizedDataBuilderTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/NormalizedNodeXmlTranslationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RetestModel.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug2690Fix.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug3674Test.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConcurrentTreeModificationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConfigStatementValidationTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ErrorReportingTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ListConstraintsValidation.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ModificationMetadataTreeTest.java [new file with mode: 0644]
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_StoreTreeNodesTest.java [new file with mode: 0644]
yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/RetestUtils.java [new file with mode: 0644]
yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsNegativeTest.java [new file with mode: 0644]
yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsTest.java [new file with mode: 0644]
yang/yang-maven-plugin-it/src/test/java/org/opendaylight/yangtools/yang2sources/plugin/it/YangToSourcesPluginTestIT.java
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesMojo.java
yang/yang-maven-plugin/src/main/java/org/opendaylight/yangtools/yang2sources/plugin/YangToSourcesProcessor.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangStatementParserListenerImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/util/YangModelDependencyInfo.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/repo/SharedSchemaContextFactory.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/DeclarationInTextSource.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/CrossSourceStatementReactor.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/EffectiveSchemaContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/ModuleEffectiveStatementImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/util/TextToASTTransformer.java
yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ModuleSourceTest.java [deleted file]

index 3acdfb2525e47f5de101c3ae90de49dc5c1d5a08..a5a1d935e5076843d477e24f8be19c1db43d915f 100644 (file)
@@ -7,14 +7,16 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.tree;
 
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
 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 &lt;lsedlak@cisco.com&gt;
@@ -42,10 +44,12 @@ public class BenchmarkModel {
         return BenchmarkModel.class.getResourceAsStream(resourceName);
     }
 
-    public static SchemaContext createTestContext() {
-        YangParserImpl parser = new YangParserImpl();
-        Set<Module> modules = parser.parseYangModelsFromStreams(Collections.singletonList(
+    public static SchemaContext createTestContext() throws SourceException, ReactorException {
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        SchemaContext schemaContext = reactor.buildEffective(Collections.singletonList(
             getDatastoreBenchmarkInputStream()));
-        return parser.resolveSchemaContext(modules);
+
+        return schemaContext;
     }
+
 }
index 4e22bd15d84c4284e0f86b846ebb5805a8df9102..a1f8961ec3646c8e9f8577241317ed8c0fbf33d7 100644 (file)
@@ -7,6 +7,9 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.tree;
 
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
 import java.io.IOException;
 import java.util.concurrent.TimeUnit;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -107,7 +110,7 @@ public class InMemoryDataTreeBenchmark {
     }
 
     @Setup(Level.Trial)
-    public void setup() throws DataValidationFailedException {
+    public void setup() throws DataValidationFailedException, SourceException, ReactorException {
         schemaContext = BenchmarkModel.createTestContext();
         final InMemoryDataTreeFactory factory = InMemoryDataTreeFactory.getInstance();
         datastore = factory.create();
diff --git a/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java b/integration-test/yang-runtime-tests/src/test/java/org/opendaylight/yangtools/it/yang/runtime/stmt/parser/retest/MultipleRevisionsSupportTest.java
new file mode 100644 (file)
index 0000000..f5870eb
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * 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.it.yang.runtime.stmt.parser.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableSet;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.generator.impl.BindingGeneratorImpl;
+import org.opendaylight.yangtools.sal.binding.generator.impl.ModuleContext;
+import org.opendaylight.yangtools.yang.binding.YangModuleInfo;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+
+public class MultipleRevisionsSupportTest {
+
+    public static final YangModuleInfo TOPOLOGY_OLD_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev130712.$YangModuleInfoImpl
+            .getInstance();
+    public static final YangModuleInfo TOPOLOGY_NEW_MODULE = org.opendaylight.yang.gen.v1.urn.tbd.params.xml.ns.yang.network.topology.rev131021.$YangModuleInfoImpl
+            .getInstance();
+
+    public static final Set<YangModuleInfo> DEPENDENCIES = ImmutableSet.<YangModuleInfo> builder() //
+            .addAll(TOPOLOGY_OLD_MODULE.getImportedModules()) //
+            .addAll(TOPOLOGY_NEW_MODULE.getImportedModules()).build();
+
+    @Test
+    public void dependenciesOlderNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void dependenciesNewerOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerOlderDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void newerDependenciesOlder() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void OlderNewerDependencies() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    @Test
+    public void olderDependenciesNewer() throws Exception {
+        List<InputStream> streams = ImmutableList.<InputStream> builder()//
+                .add(TOPOLOGY_OLD_MODULE.getModuleSourceStream()) //
+                .add(TOPOLOGY_NEW_MODULE.getModuleSourceStream()) //
+                .addAll(toInputStreams(DEPENDENCIES)) //
+                .build();
+        SchemaContext schemaContext = contextVerified(streams);
+        verifySchemaDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+        verifyBindingDifference(schemaContext, TOPOLOGY_OLD_MODULE, TOPOLOGY_NEW_MODULE);
+    }
+
+    private SchemaContext contextVerified(final List<InputStream> streams) throws SourceException, ReactorException {
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        return reactor.buildEffective(streams);
+    }
+
+    private void verifyBindingDifference(final SchemaContext schemaContext, final YangModuleInfo oldModule, final YangModuleInfo newModule) {
+        generatedTypesVerified(schemaContext, oldModule, newModule);
+    }
+
+    private Map<Module, ModuleContext> generatedTypesVerified(final SchemaContext schemaContext, final YangModuleInfo oldModule,
+            final YangModuleInfo newModule) {
+        BindingGeneratorImpl generator = new BindingGeneratorImpl();
+        generator.generateTypes(schemaContext);
+        return generator.getModuleContexts();
+    }
+
+    private void verifySchemaDifference(final SchemaContext context, final YangModuleInfo topologyOldModule,
+            final YangModuleInfo topologyNewModule) {
+        Module oldModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_OLD_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_OLD_MODULE.getRevision()));
+
+        Module newModel = context.findModuleByNamespaceAndRevision(//
+                URI.create(TOPOLOGY_NEW_MODULE.getNamespace()), QName.parseRevision(TOPOLOGY_NEW_MODULE.getRevision()));
+
+        SchemaNode oldNode = findSchemaNode(oldModel, "network-topology", "topology", "link");
+        SchemaNode newNode = findSchemaNode(newModel, "network-topology", "topology", "link");
+
+        assertNotNull(oldNode);
+        assertNotNull(newNode);
+
+        assertDeepRevision(TOPOLOGY_OLD_MODULE.getRevision(), oldNode);
+        assertDeepRevision(TOPOLOGY_NEW_MODULE.getRevision(), newNode);
+    }
+
+    private static void assertDeepRevision(final String revision, final SchemaNode node) {
+        assertEquals("Wrong revision: " + node.getPath(), revision, node.getQName().getFormattedRevision());
+        if (node instanceof DataNodeContainer) {
+            for (DataSchemaNode child : ((DataNodeContainer) node).getChildNodes()) {
+                assertDeepRevision(revision, child);
+            }
+        } else if (node instanceof ChoiceSchemaNode) {
+            for (DataSchemaNode child : ((ChoiceSchemaNode) node).getCases()) {
+                assertDeepRevision(revision, child);
+            }
+        }
+    }
+
+    private static final SchemaNode findSchemaNode(final DataNodeContainer container, final String... pathArgs) {
+        DataNodeContainer previous = container;
+
+        SchemaNode result = (container instanceof SchemaNode) ? (SchemaNode) container : null;
+        for (String arg : pathArgs) {
+            if (previous == null) {
+                return null;
+            }
+            for (DataSchemaNode child : previous.getChildNodes()) {
+                if (child.getQName().getLocalName().equals(arg)) {
+                    if (child instanceof DataNodeContainer) {
+                        previous = (DataNodeContainer) child;
+                    } else {
+                        previous = null;
+                    }
+                    result = child;
+                    break;
+                }
+            }
+        }
+        return result;
+    }
+
+    private static final Iterable<? extends InputStream> toInputStreams(final Set<YangModuleInfo> moduleInfos)
+            throws Exception {
+        Builder<InputStream> streams = ImmutableList.<InputStream> builder();
+        for (YangModuleInfo yangModuleInfo : moduleInfos) {
+            streams.add(yangModuleInfo.getModuleSourceStream());
+        }
+        return streams.build();
+    }
+
+}
index 4ae2efea6bb2e6b37a36b1fa81e91a00e6d67f6f..8844fbe7c76d29b4eb2044050f6a333a682e1b47 100644 (file)
                                     implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                     <mainClass>org.opendaylight.yangtools.yang.validation.tool.Main</mainClass>
                                 </transformer>
+                                <transformer
+                                    implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+                                    <mainClass>org.opendaylight.yangtools.yang.validation.tool.retest.Main</mainClass>
+                                </transformer>
                             </transformers>
                             <shadedArtifactAttached>true</shadedArtifactAttached>
                             <shadedClassifierName>executable</shadedClassifierName>
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Main.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Main.java
new file mode 100644 (file)
index 0000000..d5ce828
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.validation.tool.retest;
+
+import java.io.File;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+
+public final class Main {
+    private Main() {
+
+    }
+
+    public static void main(final String[] args) throws URISyntaxException {
+        final Params params = ParamsUtil.parseArgs(args, Params.getParser());
+
+        if (params.isValid()) {
+            final File[] yangModels = params.getYangSourceDir().listFiles();
+
+            try {
+                RetestUtils.parseYangSources(Arrays.asList(yangModels));
+            } catch (Exception e) {
+
+            }
+        }
+    }
+}
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Params.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/Params.java
new file mode 100644 (file)
index 0000000..269a5b0
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.validation.tool.retest;
+
+import java.io.File;
+import java.net.URISyntaxException;
+
+import net.sourceforge.argparse4j.ArgumentParsers;
+import net.sourceforge.argparse4j.annotation.Arg;
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+
+final class Params {
+
+    @Arg(dest = "yang-source-dir")
+    private File yangSourceDir;
+
+    static ArgumentParser getParser() throws URISyntaxException {
+        final ArgumentParser parser = ArgumentParsers.newArgumentParser("jar_file_name");
+        parser.description("Validation Tool for Yang Models")
+            .formatUsage();
+
+        parser.addArgumentGroup("Required arguments")
+            .addArgument("--yang-source-dir")
+            .type(File.class)
+            .required(true)
+            .help("directory containing yang models which will be parsed")
+            .dest("yang-source-dir")
+            .metavar("");
+
+        return parser;
+    }
+
+    public boolean isValid() {
+        if (yangSourceDir == null) {
+            return false;
+        }
+        if (!yangSourceDir.exists()) {
+            System.err.println("Yang source directory has to exist");
+            return false;
+        }
+        if (!yangSourceDir.canRead()) {
+            System.err.println("Yang source directory has to be readable");
+            return false;
+        }
+        if (yangSourceDir.list().length == 0) {
+            System.err.printf("Yang source directory '%s' does't contain any model%n", yangSourceDir.getPath());
+            return false;
+        }
+
+        return true;
+    }
+
+    public File getYangSourceDir() {
+        return yangSourceDir;
+    }
+}
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/ParamsUtil.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/ParamsUtil.java
new file mode 100644 (file)
index 0000000..bed5a15
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.validation.tool.retest;
+
+import net.sourceforge.argparse4j.inf.ArgumentParser;
+import net.sourceforge.argparse4j.inf.ArgumentParserException;
+
+final class ParamsUtil {
+    private ParamsUtil() {
+
+    }
+
+    public static Params parseArgs(final String[] args, final ArgumentParser parser) {
+        final Params params = new Params();
+        try {
+            parser.parseArgs(args, params);
+            return params;
+        } catch (final ArgumentParserException e) {
+            parser.handleError(e);
+        }
+        System.exit(1);
+        return null;
+    }
+}
diff --git a/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/RetestUtils.java b/yang-validation-tool/src/main/java/org/opendaylight/yangtools/yang/validation/tool/retest/RetestUtils.java
new file mode 100644 (file)
index 0000000..2a9eda9
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.validation.tool.retest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class RetestUtils {
+
+    private RetestUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static SchemaContext parseYangSources(StatementStreamSource... sources) throws SourceException,
+            ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        reactor.addSources(sources);
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(File... files) throws SourceException, ReactorException,
+            FileNotFoundException {
+
+        StatementStreamSource[] sources = new StatementStreamSource[files.length];
+
+        for (int i = 0; i < files.length; i++) {
+            sources[i] = new YangStatementSourceImpl(new FileInputStream(files[i]));
+        }
+
+        return parseYangSources(sources);
+    }
+
+    public static SchemaContext parseYangSources(Collection<File> files) throws SourceException, ReactorException,
+            FileNotFoundException {
+        return parseYangSources(files.toArray(new File[files.size()]));
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/JsonStreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/JsonStreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..00178ef
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadTextFile;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+
+import com.google.common.collect.Sets;
+import com.google.gson.stream.JsonReader;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.net.URISyntaxException;
+
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+/**
+ *
+ * Each test tests whether json input is correctly transformed to normalized node structure
+ */
+public class JsonStreamToNormalizedNodeTest {
+
+    private static final QName CONT_1 = QName.create("ns:complex:json", "2014-08-11", "cont1");
+    private static final QName EMPTY_LEAF = QName.create(CONT_1,"empty");
+    private static SchemaContext schemaContext;
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException, ReactorException {
+        schemaContext = loadModules("/complexjson/yang");
+    }
+
+    /**
+     * case when anyxml contains simple value will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    /**
+     * case when anyxml contains complex xml will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    @Test
+    public void leafNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaf-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.leafNodeInContainer());
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaf-node-via-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafNodeViaAugmentationInContainer());
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/leaflist-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.leafListNodeInContainer());
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.keyedListNodeInContainer());
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/choice-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.choiceNodeInContainer());
+    }
+
+    /**
+     * Test of translating internal augmentations to normalized nodes structure
+     *
+     * 2 nodes are added via internal augmentation A, 1 node via internal augmentation B and one node is originally
+     * member of case.
+     *
+     */
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/case-node-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * also test using of namesakes (equal local names with different
+     *
+     * @throws IOException
+     * @throws URISyntaxException
+     */
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/case-node-external-augmentation-in-choice-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.caseNodeExternalAugmentationInChoiceInContainer());
+    }
+
+    /**
+     * augmentation of choice - adding new case
+     */
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/choice-node-augmentation-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson,
+                TestingNormalizedNodeStructuresCreator.choiceNodeAugmentationInContainer());
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/unkeyed-node-in-container.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
+    }
+
+    /**
+     * Top level JSON element contains no information about module name.
+     *
+     * It should be possible to find out potential module name from available schema context.
+     *
+     */
+    @Test
+    public void missingModuleInfoInTopLevelElement() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/missing-module-in-top-level.json");
+        verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.topLevelContainer());
+    }
+
+    /**
+     *
+     * Exception expected.
+     *
+     * It tests case when several elements with the same name and various namespaces exists and are in JSON specified
+     * without module name prefix.
+     */
+    @Test
+    public void leafNamesakes() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/namesakes.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+                       fail("Expected exception not raised");
+        } catch (final IllegalStateException e) {
+            final String errorMessage = e.getMessage();
+            assertTrue(errorMessage.contains("Choose suitable module name for element lf11-namesake:"));
+            assertTrue(errorMessage.contains("complexjson-augmentation"));
+            assertTrue(errorMessage.contains("complexjson-augmentation-namesake"));
+        }
+    }
+
+    @Test
+    public void emptyTypeTest() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/type-empty.json");
+        final ContainerNode awaitedStructure = containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CONT_1))
+                .addChild(leafNode(EMPTY_LEAF, null))
+                .build();
+
+        verifyTransformationToNormalizedNode(inputJson, awaitedStructure);
+    }
+
+    /**
+     *
+     * Exception expected.
+     *
+     * Json input contains element which doesn't exist in YANG schema
+     */
+    @Test
+    public void parsingNotExistingElement() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/not-existing-element.json");
+        try {
+            //second parameter isn't necessary because error will be raised before it is used.
+            verifyTransformationToNormalizedNode(inputJson, null);
+        } catch (final IllegalStateException e) {
+            assertTrue(e.getMessage().contains("Schema node with name dummy-element wasn't found"));
+        }
+    }
+
+
+    @Test
+    public void listItemWithoutArray() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-restconf-behaviour.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext, parentNode);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+    @Test
+    public void listItemWithArray() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/keyed-list-yang-json-behaviour.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext, parentNode);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+    }
+
+   @Test
+    public void multipleChoiceAugmentation() throws IOException, URISyntaxException {
+        final String inputJson = loadTextFile("/complexjson/multiple-choice-augmentation-in-container.json");
+
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final SchemaNode parentNode = schemaContext.getDataChildByName("cont1");
+
+        QName augmentChoice1QName = QName.create(parentNode.getQName(), "augment-choice1");
+        QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
+        final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
+
+        final YangInstanceIdentifier.AugmentationIdentifier aug1Id =
+                new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(augmentChoice1QName));
+        final YangInstanceIdentifier.AugmentationIdentifier aug2Id =
+                new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(augmentChoice2QName));
+        final YangInstanceIdentifier.NodeIdentifier augmentChoice1Id =
+                new YangInstanceIdentifier.NodeIdentifier(augmentChoice1QName);
+        final YangInstanceIdentifier.NodeIdentifier augmentChoice2Id =
+                new YangInstanceIdentifier.NodeIdentifier(augmentChoice2QName);
+        final YangInstanceIdentifier.NodeIdentifier containerId =
+                new YangInstanceIdentifier.NodeIdentifier(containerQName);
+
+        final NormalizedNode<?, ?> cont1Normalized =
+                containerBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(parentNode.getQName()))
+                        .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
+                                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
+                                        .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
+                                                .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
+                                                        .withChild(containerBuilder().withNodeIdentifier(containerId)
+                                                                .withChild(leafNode(leafQName, "leaf-value"))
+                                                                .build())
+                                                        .build())
+                                                .build())
+                                        .build())
+                                .build()).build();
+
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertNotNull(transformedInput);
+        assertEquals(cont1Normalized, transformedInput);
+    }
+
+    private void verifyTransformationToNormalizedNode(final String inputJson,
+            final NormalizedNode<?, ?> awaitedStructure) {
+        final NormalizedNodeResult result = new NormalizedNodeResult();
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+        final JsonParserStream jsonParser = JsonParserStream.create(streamWriter, schemaContext);
+        jsonParser.parse(new JsonReader(new StringReader(inputJson)));
+        final NormalizedNode<?, ?> transformedInput = result.getResult();
+        assertEquals("Transformation of json input to normalized node wasn't successful.", awaitedStructure,
+                transformedInput);
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/NormalizedNodeToJsonStreamTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/NormalizedNodeToJsonStreamTest.java
new file mode 100644 (file)
index 0000000..bf0417f
--- /dev/null
@@ -0,0 +1,427 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.childArray;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.childPrimitive;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.resolveCont1;
+
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+import com.google.common.collect.Sets;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonNull;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonPrimitive;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.net.URISyntaxException;
+import java.util.HashSet;
+import java.util.Iterator;
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+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.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+/**
+ * Each test tests whether json output obtained after transformation contains is corect. The transformation takes
+ * normalized node data structure and transform it to json output. To make it easier validate json output it is loaded
+ * via gson as structure of json elements which are walked and compared with awaited values.
+ *
+ */
+public class NormalizedNodeToJsonStreamTest {
+
+    private static final QName CONT_1 = QName.create("ns:complex:json", "2014-08-11", "cont1");
+    private static final QName EMPTY_LEAF = QName.create(CONT_1, "empty");
+    private static SchemaContext schemaContext;
+
+    public interface JsonValidator {
+        void validate(final String jsonOutput);
+    }
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException, ReactorException {
+        schemaContext = loadModules("/complexjson/yang");
+    }
+
+    /**
+     * case when anyxml contains simple value will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithSimpleValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    /**
+     * case when anyxml contains complex xml will be implemented when anyxml normalized node reprezentation will be
+     * specified
+     */
+    @Ignore
+    @Test
+    public void anyXmlNodeWithCompositeValueInContainer() throws IOException, URISyntaxException {
+
+    }
+
+    @Test
+    public void leafNodeInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> leafNodeInContainer = TestingNormalizedNodeStructuresCreator.leafNodeInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonPrimitive lf11 = childPrimitive(cont1, "complexjson:lf11", "lf11");
+                assertNotNull(lf11);
+                final int asInt = lf11.getAsInt();
+                assertEquals(453, asInt);
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void leafListNodeInContainerMultiline() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> leafListNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .leafListNodeInContainerMultiline();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafListNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                final JsonArray lflst11 = childArray(cont1, "complexjson:lflst11", "lflst11");
+                assertNotNull(lflst11);
+
+                final HashSet<Object> lflst11Values = Sets.newHashSet();
+                for (final JsonElement jsonElement : lflst11) {
+                    assertTrue(jsonElement instanceof JsonPrimitive);
+                    lflst11Values.add(((JsonPrimitive) jsonElement).getAsString());
+                }
+
+                assertEquals(Sets.newHashSet("lflst11 value2\r\nanother line 2", "lflst11 value1\nanother line 1"),
+                        lflst11Values);
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void leafNodeViaAugmentationInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> leafNodeViaAugmentationInContainer = TestingNormalizedNodeStructuresCreator
+                .leafNodeViaAugmentationInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafNodeViaAugmentationInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonPrimitive lf12_1 = childPrimitive(cont1, "complexjson:lf12_1", "lf12_1");
+                assertNotNull(lf12_1);
+                final String asString = lf12_1.getAsString();
+                assertEquals("lf12 value", asString);
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void leafListNodeInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> leafListNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .leafListNodeInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, leafListNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                final JsonArray lflst11 = childArray(cont1, "complexjson:lflst11", "lflst11");
+                assertNotNull(lflst11);
+
+                final HashSet<Object> lflst11Values = Sets.newHashSet();
+                for (final JsonElement jsonElement : lflst11) {
+                    assertTrue(jsonElement instanceof JsonPrimitive);
+                    lflst11Values.add(((JsonPrimitive) jsonElement).getAsString());
+                }
+
+                assertEquals(Sets.newHashSet("lflst11 value2", "lflst11 value1"), lflst11Values);
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void keyedListNodeInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> keyedListNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .keyedListNodeInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, keyedListNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                final JsonArray lst11 = childArray(cont1, "complexjson:lst11", "lst11");
+                assertNotNull(lst11);
+
+                final Iterator<JsonElement> iterator = lst11.iterator();
+                assertTrue(iterator.hasNext());
+                final JsonElement lst11Entry1Raw = iterator.next();
+                assertFalse(iterator.hasNext());
+                assertTrue(lst11Entry1Raw instanceof JsonObject);
+                final JsonObject lst11Entry1 = (JsonObject) lst11Entry1Raw;
+
+                final JsonPrimitive key111 = childPrimitive(lst11Entry1, "complexjson:key111", "key111");
+                assertNotNull(key111);
+                final JsonPrimitive lf112 = childPrimitive(lst11Entry1, "complexjson:lf112", "lf112");
+                assertNotNull(lf112);
+                final JsonPrimitive lf113 = childPrimitive(lst11Entry1, "complexjson:lf113", "lf113");
+                assertNotNull(lf113);
+                final JsonPrimitive lf111 = childPrimitive(lst11Entry1, "complexjson:lf111", "lf111");
+                assertNotNull(lf111);
+
+                assertEquals("key111 value", key111.getAsString());
+                assertEquals("/complexjson:cont1/complexjson:lflst11[.='foo']", lf112.getAsString());
+                assertEquals("lf113 value", lf113.getAsString());
+                assertEquals("lf111 value", lf111.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void choiceNodeInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> choiceNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .choiceNodeInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, choiceNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+                final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+
+                assertEquals("lf13 value", lf13.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) and one leaf (augment B) are
+     * added)
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+    // @Ignore
+    @Test
+    public void caseNodeAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> caseNodeAugmentationInChoiceInContainer = TestingNormalizedNodeStructuresCreator
+                .caseNodeAugmentationInChoiceInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer,
+                caseNodeAugmentationInChoiceInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonPrimitive lf15_21 = childPrimitive(cont1, "complexjson:lf15_21", "lf15_21");
+                assertNotNull(lf15_21);
+                final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+                final JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+                assertNotNull(lf15_11);
+                final JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+                assertNotNull(lf15_12);
+
+                assertEquals("lf15_21 value", lf15_21.getAsString());
+                assertEquals("lf13 value", lf13.getAsString());
+                assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+                assertEquals("complexjson:lf11", lf15_12.getAsString());
+
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * tested case when case c11A in choice choc11 is augmented (two leaves (augment A) internally and one two leaves
+     * with the same names externally (augment B) are added)
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+    // @Ignore
+    @Test
+    public void caseNodeExternalAugmentationInChoiceInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> caseNodeExternalAugmentationInChoiceInContainer = TestingNormalizedNodeStructuresCreator
+                .caseNodeExternalAugmentationInChoiceInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer,
+                caseNodeExternalAugmentationInChoiceInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonPrimitive lf15_11Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_11");
+                assertNotNull(lf15_11Augment);
+                final JsonPrimitive lf15_12Augment = childPrimitive(cont1, "complexjson-augmentation:lf15_12");
+                assertNotNull(lf15_12Augment);
+                final JsonPrimitive lf13 = childPrimitive(cont1, "complexjson:lf13", "lf13");
+                assertNotNull(lf13);
+                final JsonPrimitive lf15_11 = childPrimitive(cont1, "complexjson:lf15_11", "lf15_11");
+                assertNotNull(lf15_11);
+                final JsonPrimitive lf15_12 = childPrimitive(cont1, "complexjson:lf15_12", "lf15_12");
+                assertNotNull(lf15_12);
+
+                assertEquals("lf15_11 value from augmentation", lf15_11Augment.getAsString());
+                assertEquals("lf15_12 value from augmentation", lf15_12Augment.getAsString());
+                assertEquals("lf13 value", lf13.getAsString());
+                assertTrue("one two".equals(lf15_11.getAsString()) || "two one".equals(lf15_11.getAsString()));
+                assertEquals("complexjson:lf11", lf15_12.getAsString());
+
+            }
+        }.validate(jsonOutput);
+    }
+
+    /**
+     * augmentation of choice - adding new case
+     *
+     * after running this test following exception is raised
+     *
+     * java.lang.IllegalArgumentException: Augmentation allowed only in DataNodeContainer
+     * [ChoiceNodeImpl[qname=(ns:complex:json?revision=2014-08-11)choc11]]
+     *
+     */
+    // @Ignore
+    @Test
+    public void choiceNodeAugmentationInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> choiceNodeAugmentationInContainer = TestingNormalizedNodeStructuresCreator
+                .choiceNodeAugmentationInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, choiceNodeAugmentationInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonPrimitive lf17 = childPrimitive(cont1, "complexjson:lf17", "lf17");
+                assertNotNull(lf17);
+                assertEquals("lf17 value", lf17.getAsString());
+            }
+        }.validate(jsonOutput);
+    }
+
+    @Test
+    public void unkeyedNodeInContainer() throws IOException, URISyntaxException {
+        final Writer writer = new StringWriter();
+        final NormalizedNode<?, ?> unkeyedNodeInContainer = TestingNormalizedNodeStructuresCreator
+                .unkeyedNodeInContainer();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, unkeyedNodeInContainer);
+        new JsonValidator() {
+
+            @Override
+            public void validate(final String jsonOutput) {
+                final JsonObject cont1 = resolveCont1(jsonOutput);
+                assertNotNull(cont1);
+
+                final JsonArray lst12 = childArray(cont1, "complexjson:lst12", "lst12");
+                assertNotNull(lst12);
+
+                final Iterator<JsonElement> iterator = lst12.iterator();
+                assertTrue(iterator.hasNext());
+                final JsonElement lst12Entry1Raw = iterator.next();
+                assertFalse(iterator.hasNext());
+
+                assertTrue(lst12Entry1Raw instanceof JsonObject);
+                final JsonObject lst12Entry1 = (JsonObject) lst12Entry1Raw;
+                final JsonPrimitive lf121 = childPrimitive(lst12Entry1, "complexjson:lf121", "lf121");
+                assertNotNull(lf121);
+
+                assertEquals("lf121 value", lf121.getAsString());
+
+            }
+        }.validate(jsonOutput);
+
+    }
+
+    @Test
+    public void emptyTypeTest() throws IOException, URISyntaxException {
+        final StringWriter writer = new StringWriter();
+        final ContainerNode emptyStructure = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(CONT_1))
+                .addChild(ImmutableNodes.leafNode(EMPTY_LEAF, null)).build();
+        final String jsonOutput = normalizedNodeToJsonStreamTransformation(writer, emptyStructure);
+        final JsonObject cont1 = resolveCont1(jsonOutput);
+        final JsonElement emptyObj = cont1.get("empty");
+        assertNotNull(emptyObj);
+        assertTrue(emptyObj instanceof JsonArray);
+        assertEquals(1, emptyObj.getAsJsonArray().size());
+        assertTrue(emptyObj.getAsJsonArray().get(0) instanceof JsonNull);
+    }
+
+    private String normalizedNodeToJsonStreamTransformation(final Writer writer,
+            final NormalizedNode<?, ?> inputStructure) throws IOException {
+
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.
+                createExclusiveWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null,
+                    JsonWriterFactory.createJsonWriter(writer, 2));
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+        nodeWriter.write(inputStructure);
+
+        nodeWriter.close();
+        return writer.toString();
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/RetestUtils.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/RetestUtils.java
new file mode 100644 (file)
index 0000000..92e20a2
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class RetestUtils {
+
+    private RetestUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static SchemaContext parseYangSources(StatementStreamSource... sources)
+            throws SourceException, ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        reactor.addSources(sources);
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(File... files) throws SourceException, ReactorException, FileNotFoundException {
+
+        StatementStreamSource[] sources = new StatementStreamSource[files.length];
+
+        for(int i = 0; i<files.length; i++) {
+            sources[i] = new YangStatementSourceImpl(new FileInputStream(files[i]));
+        }
+
+        return parseYangSources(sources);
+    }
+
+    public static SchemaContext parseYangSources(Collection<File> files) throws SourceException, ReactorException, FileNotFoundException {
+        return parseYangSources(files.toArray(new File[files.size()]));
+    }
+
+
+    public static SchemaContext parseYangStreams(List<InputStream> streams)
+            throws SourceException, ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        return reactor.buildEffective(streams);
+    }
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/StreamToNormalizedNodeTest.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/StreamToNormalizedNodeTest.java
new file mode 100644 (file)
index 0000000..d19c7aa
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadModules;
+import static org.opendaylight.yangtools.yang.data.codec.gson.retest.TestUtils.loadTextFile;
+
+import org.opendaylight.yangtools.yang.data.codec.gson.JSONCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonWriterFactory;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+
+import com.google.gson.stream.JsonReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+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.codec.gson.JSONNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.JsonParserStream;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedNodeResult;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StreamToNormalizedNodeTest {
+    private static final Logger LOG = LoggerFactory.getLogger(StreamToNormalizedNodeTest.class);
+    private static SchemaContext schemaContext;
+    private static String streamAsString;
+
+    @BeforeClass
+    public static void initialization() throws IOException, URISyntaxException, ReactorException {
+        schemaContext = loadModules("/complexjson/yang");
+        streamAsString = loadTextFile(new File(StreamToNormalizedNodeTest.class.getResource(
+                "/complexjson/complex-json.json").toURI()));
+    }
+
+    /**
+     * Demonstrates how to log events produced by a {@link JsonReader}.
+     *
+     * @throws IOException
+     */
+    @Test
+    public void ownStreamWriterImplementationDemonstration() throws IOException {
+        // GSON's JsonReader reading from the loaded string (our event source)
+        final JsonReader reader = new JsonReader(new StringReader(streamAsString));
+
+        // StreamWriter which outputs SLF4J events
+        final LoggingNormalizedNodeStreamWriter logWriter = new LoggingNormalizedNodeStreamWriter();
+
+        // JSON -> StreamWriter parser
+        try (final JsonParserStream jsonHandler = JsonParserStream.create(logWriter, schemaContext)) {
+            // Process multiple readers, flush()/close() as needed
+            jsonHandler.parse(reader);
+        }
+    }
+
+    /**
+     * Demonstrates how to create an immutable NormalizedNode tree from a {@link JsonReader} and
+     * then writes the data back into string representation.
+     *
+     * @throws IOException
+     */
+    @Ignore
+    @Test
+    public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException {
+        /*
+         * This is the parsing part
+         */
+        // This is where we will output the nodes
+        NormalizedNodeResult result = new NormalizedNodeResult();
+
+        // StreamWriter which attaches NormalizedNode under parent
+        final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(result);
+
+        // JSON -> StreamWriter parser
+        try (JsonParserStream handler = JsonParserStream.create(streamWriter, schemaContext)) {
+            handler.parse(new JsonReader(new StringReader(streamAsString)));
+        }
+
+        // Finally build the node
+        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<? extends PathArgument, ?> firstChild = (DataContainerChild<? extends PathArgument, ?>) parsedData;
+        LOG.debug("Serializing first child: {}", firstChild);
+
+        // String holder
+        final StringWriter writer = new StringWriter();
+
+        // StreamWriter which outputs JSON strings
+        // StreamWriter which outputs JSON strings
+        final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.
+                createExclusiveWriter(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, null,
+                    JsonWriterFactory.createJsonWriter(writer, 2));
+
+        // NormalizedNode -> StreamWriter
+        final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+
+        // Write multiple NormalizedNodes fluently, flush()/close() as needed
+        nodeWriter.write(firstChild).close();
+
+        // Just to put it somewhere
+        LOG.debug("Serialized JSON: {}", writer.toString());
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestUtils.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestUtils.java
new file mode 100644 (file)
index 0000000..43fb8ae
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import static org.junit.Assert.assertTrue;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonPrimitive;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class TestUtils {
+
+    private TestUtils() {
+    }
+
+    static SchemaContext loadModules(final String resourceDirectory) throws IOException, URISyntaxException,
+            ReactorException {
+        URI path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).toURI();
+        final File testDir = new File(path);
+        final String[] fileList = testDir.list();
+        final List<File> testFiles = new ArrayList<File>();
+        if (fileList == null) {
+            throw new FileNotFoundException(resourceDirectory);
+        }
+        for (String fileName : fileList) {
+            if (new File(testDir, fileName).isDirectory() == false) {
+                testFiles.add(new File(testDir, fileName));
+            }
+        }
+        return RetestUtils.parseYangSources(testFiles);
+    }
+
+    static String loadTextFile(final File file) throws IOException {
+        FileReader fileReader = new FileReader(file);
+        BufferedReader bufReader = new BufferedReader(fileReader);
+
+        String line = null;
+        StringBuilder result = new StringBuilder();
+        while ((line = bufReader.readLine()) != null) {
+            result.append(line);
+        }
+        bufReader.close();
+        return result.toString();
+    }
+
+    static String loadTextFile(final String relativePath) throws IOException, URISyntaxException {
+        return loadTextFile(new File(TestUtils.class.getResource(relativePath).toURI()));
+    }
+
+    static JsonObject childObject(final JsonObject jsonObject, final String... names) {
+        for (String name : names) {
+            JsonObject childJsonObject = jsonObject.getAsJsonObject(name);
+            if (childJsonObject != null) {
+                return childJsonObject;
+            }
+        }
+        return null;
+    }
+
+    static JsonPrimitive childPrimitive(final JsonObject jsonObject, final String... names) {
+        for (String name : names) {
+            JsonPrimitive childJsonPrimitive = jsonObject.getAsJsonPrimitive(name);
+            if (childJsonPrimitive != null) {
+                return childJsonPrimitive;
+            }
+        }
+        return null;
+    }
+
+    static JsonArray childArray(final JsonObject jsonObject, final String... names) {
+        for (String name : names) {
+            JsonArray childJsonArray = jsonObject.getAsJsonArray(name);
+            if (childJsonArray != null) {
+                return childJsonArray;
+            }
+        }
+        return null;
+    }
+
+    static JsonObject resolveCont1(String jsonOutput) {
+        JsonParser parser = new JsonParser();
+        JsonElement rootElement = parser.parse(jsonOutput);
+        assertTrue(rootElement.isJsonObject());
+        JsonObject rootObject = rootElement.getAsJsonObject();
+        JsonObject cont1 = childObject(rootObject, "complexjson:cont1", "cont1");
+        return cont1;
+    }
+
+}
diff --git a/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestingNormalizedNodeStructuresCreator.java b/yang/yang-data-codec-gson/src/test/java/org/opendaylight/yangtools/yang/data/codec/gson/retest/TestingNormalizedNodeStructuresCreator.java
new file mode 100644 (file)
index 0000000..d2fa878
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.codec.gson.retest;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+
+import java.util.HashMap;
+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.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+
+public class TestingNormalizedNodeStructuresCreator {
+
+    static NormalizedNode<?, ?> cont1Node(
+            DataContainerChild<? extends PathArgument, ?>... children) {
+        DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> cont1 = Builders.containerBuilder();
+        cont1.withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "cont1")));
+
+        cont1.withValue(Lists.newArrayList(children));
+        return cont1.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> lst12Node() {
+        CollectionNodeBuilder<UnkeyedListEntryNode, UnkeyedListNode> lst12Builder = Builders.unkeyedListBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst12")));
+        lst12Builder.withChild(lst12Entry1Node());
+        return lst12Builder.build();
+    }
+
+    private static UnkeyedListEntryNode lst12Entry1Node() {
+        DataContainerNodeAttrBuilder<NodeIdentifier, UnkeyedListEntryNode> lst12Entry1Builder = Builders
+                .unkeyedListEntryBuilder();
+        lst12Entry1Builder
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst12")));
+        lst12Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf121")))
+                .withValue("lf121 value").build());
+        return lst12Entry1Builder.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> choc12Node() {
+        DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choc12Builder = Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "choc12")));
+
+        choc12Builder.withChild(lf17Node());
+        return choc12Builder.build();
+    }
+
+    protected static LeafNode<Object> lf17Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf17")))
+                .withValue("lf17 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> externalAugmentC11AWithLf15_11AndLf15_12Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentationBuilder = Builders
+                .augmentationBuilder();
+        augmentationBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(
+                QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_11"),
+                QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_12"))));
+        augmentationBuilder.withChild(lf15_11NodeExternal());
+        augmentationBuilder.withChild(lf15_12NodeExternal());
+        return augmentationBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_12NodeExternal() {
+        return Builders
+                .leafBuilder()
+                .withNodeIdentifier(
+                        new NodeIdentifier(QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_12")))
+                .withValue("lf15_12 value from augmentation").build();
+    }
+
+    private static LeafNode<Object> lf15_11NodeExternal() {
+        return Builders
+                .leafBuilder()
+                .withNodeIdentifier(
+                        new NodeIdentifier(QName.create("ns:complex:json:augmentation", "2014-08-14", "lf15_11")))
+                .withValue("lf15_11 value from augmentation").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> choc11Node(
+            DataContainerChild<? extends PathArgument, ?>... children) {
+        DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choc11Builder = Builders.choiceBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "choc11")));
+        choc11Builder.withValue(Lists.newArrayList(children));
+        // choc11Builder.addChild(lf13Node());
+        // choc11Builder.addChild(augmentChoc11_c11A_lf1511AndLf1512Children());
+        // choc11Builder.addChild(augmentChoc11_c11_lf1521Children());
+        return choc11Builder.build();
+    }
+
+    private static LeafNode<Object> lf13Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf13")))
+                .withValue("lf13 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> augmentC11AWithLf15_21Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> choc11_c11AugmentBuilder = Builders
+                .augmentationBuilder();
+        choc11_c11AugmentBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(QName.create(
+                "ns:complex:json", "2014-08-11", "lf15_21"))));
+
+        choc11_c11AugmentBuilder.withChild(lf15_21Node());
+        return choc11_c11AugmentBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_21Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_21")))
+                .withValue("lf15_21 value").build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> augmentC11AWithLf15_11AndLf15_12Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> choc11_c11AugmentBuilder = Builders
+                .augmentationBuilder();
+        choc11_c11AugmentBuilder.withNodeIdentifier(new AugmentationIdentifier(Sets.newHashSet(
+                QName.create("ns:complex:json", "2014-08-11", "lf15_11"),
+                QName.create("ns:complex:json", "2014-08-11", "lf15_12"))));
+        choc11_c11AugmentBuilder.withChild(lf15_11Node());
+        choc11_c11AugmentBuilder.withChild(lf15_12Node());
+        return choc11_c11AugmentBuilder.build();
+    }
+
+    private static LeafNode<Object> lf15_12Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_12")))
+                .withValue(QName.create("ns:complex:json", "2014-08-11", "lf11")).build();
+    }
+
+    private static LeafNode<Object> lf15_11Node() {
+        return Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf15_11")))
+                .withValue(Sets.newHashSet("one", "two")).build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> lf12_1Node() {
+        DataContainerNodeBuilder<AugmentationIdentifier, AugmentationNode> augmentBuilder = Builders
+                .augmentationBuilder().withNodeIdentifier(
+                        new AugmentationIdentifier(Sets.newHashSet(
+                                QName.create("ns:complex:json", "2014-08-11", "lf12_1"),
+                                QName.create("ns:complex:json", "2014-08-11", "lf12_2"))));
+        augmentBuilder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf12_1")))
+                .withValue("lf12 value").build());
+        return augmentBuilder.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> childLst11() {
+        CollectionNodeBuilder<MapEntryNode, MapNode> lst11 = Builders.mapBuilder().withNodeIdentifier(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lst11")));
+
+        DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> lst11Entry1Builder = Builders
+                .mapEntryBuilder();
+
+        Map<QName, Object> key = new HashMap<>();
+        key.put(QName.create("ns:complex:json", "2014-08-11", "key111"), "key111 value");
+        key.put(QName.create("ns:complex:json", "2014-08-11", "lf111"), "lf111 value");
+
+        lst11Entry1Builder.withNodeIdentifier(new NodeIdentifierWithPredicates(QName.create("ns:complex:json",
+                "2014-08-11", "lst11"), key));
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "key111")))
+                .withValue("key111 value").build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf112")))
+                .withValue(lf112Value()).build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf113")))
+                .withValue("lf113 value").build());
+        lst11Entry1Builder.withChild(Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf111")))
+                .withValue("lf111 value").build());
+        lst11.withChild(lst11Entry1Builder.build());
+        return lst11.build();
+    }
+
+    private static Object lf112Value() {
+        return YangInstanceIdentifier.create(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "cont1")),
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lflst11")),
+                new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"),"foo")
+        );
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> childLflst11() {
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> lflst11 = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lflst11")));
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value1"))
+                .withValue("lflst11 value1").build());
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value2"))
+                .withValue("lflst11 value2").build());
+        return lflst11.build();
+    }
+
+    private static DataContainerChild<? extends PathArgument, ?> childLflst11Multiline() {
+        ListNodeBuilder<Object, LeafSetEntryNode<Object>> lflst11 = Builders.leafSetBuilder().withNodeIdentifier(
+                new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lflst11")));
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value1\nanother line 1"))
+                .withValue("lflst11 value1\nanother line 1").build());
+        lflst11.withChild(Builders
+                .leafSetEntryBuilder()
+                .withNodeIdentifier(
+                        new NodeWithValue(QName.create("ns:complex:json", "2014-08-11", "lflst11"), "lflst11 value2\r\nanother line 2"))
+                .withValue("lflst11 value2\r\nanother line 2").build());
+        return lflst11.build();
+    }
+
+    public static NormalizedNode<?, ?> leafNodeInContainer() {
+        LeafNode<Object> lf11 = Builders.leafBuilder()
+                .withNodeIdentifier(new NodeIdentifier(QName.create("ns:complex:json", "2014-08-11", "lf11")))
+                .withValue(453).build();
+        return cont1Node(lf11);
+    }
+
+    public static NormalizedNode<?, ?> leafListNodeInContainer() {
+        return cont1Node(childLflst11());
+    }
+    public static NormalizedNode<?, ?> leafListNodeInContainerMultiline() {
+        return cont1Node(childLflst11Multiline());
+    }
+
+    public static NormalizedNode<?, ?> keyedListNodeInContainer() {
+        return cont1Node(childLst11());
+    }
+
+    public static NormalizedNode<?, ?> leafNodeViaAugmentationInContainer() {
+        return cont1Node(lf12_1Node());
+    }
+
+    public static NormalizedNode<?, ?> choiceNodeInContainer() {
+        return cont1Node(choc11Node(lf13Node()));
+    }
+
+    /**
+     * choc11 contains lf13, lf15_11 and lf15_12 are added via external augmentation
+     *
+     * @return
+     */
+    public static NormalizedNode<?, ?> caseNodeAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(augmentC11AWithLf15_11AndLf15_12Node(), lf13Node(), augmentC11AWithLf15_21Node()));
+    }
+
+    public static NormalizedNode<?, ?> caseNodeExternalAugmentationInChoiceInContainer() {
+        return cont1Node(choc11Node(lf13Node(), augmentC11AWithLf15_11AndLf15_12Node(), externalAugmentC11AWithLf15_11AndLf15_12Node()));
+    }
+
+    public static NormalizedNode<?, ?> choiceNodeAugmentationInContainer() {
+        return cont1Node(choc12Node());
+    }
+
+    public static NormalizedNode<?, ?> unkeyedNodeInContainer() {
+        return cont1Node(lst12Node());
+    }
+
+    public static NormalizedNode<?, ?> topLevelContainer() {
+        return cont1Node();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefix.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefix.java
new file mode 100644 (file)
index 0000000..dfe7dd5
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+
+class RandomPrefix {
+    // 32 characters, carefully chosen
+    private static final String LOOKUP = "abcdefghiknoprstABCDEFGHIKNOPRST";
+    private static final int MASK = 0x1f;
+    private static final int SHIFT = 5;
+
+    private int counter = 0;
+
+    // BiMap to make values lookup faster
+    private final BiMap<URI, String> prefixes = HashBiMap.create();
+    private final NamespaceContext context;
+
+    RandomPrefix() {
+        this.context = null;
+    }
+
+    RandomPrefix(final NamespaceContext context) {
+        this.context = Preconditions.checkNotNull(context);
+    }
+
+    Iterable<Map.Entry<URI, String>> getPrefixes() {
+        return prefixes.entrySet();
+    }
+
+    String encodePrefix(final URI namespace) {
+        String prefix = prefixes.get(namespace);
+        if (prefix != null) {
+            return prefix;
+        }
+
+        do {
+            prefix = encode(counter);
+            counter++;
+        } while (alreadyUsedPrefix(prefix));
+
+        prefixes.put(namespace, prefix);
+        return prefix;
+    }
+
+    private boolean alreadyUsedPrefix(final String prefix) {
+        if (context == null) {
+            return false;
+        }
+
+        final String str = context.getNamespaceURI(prefix);
+        return !XMLConstants.NULL_NS_URI.equals(str);
+    }
+
+    @VisibleForTesting
+    static int decode(final String str) {
+        int ret = 0;
+        for (char c : str.toCharArray()) {
+            int idx = LOOKUP.indexOf(c);
+            Preconditions.checkArgument(idx != -1, "Invalid string %s", str);
+            ret = (ret << SHIFT) + idx;
+        }
+
+        return ret;
+    }
+
+    @VisibleForTesting
+    static String encode(int num) {
+        final StringBuilder sb = new StringBuilder();
+
+        do {
+            sb.append(LOOKUP.charAt(num & MASK));
+            num >>>= SHIFT;
+        } while (num != 0);
+
+        return sb.reverse().toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixInstanceIdentifierSerializer.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixInstanceIdentifierSerializer.java
new file mode 100644 (file)
index 0000000..d99e7ef
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import java.net.URI;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.util.AbstractStringInstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+final class RandomPrefixInstanceIdentifierSerializer extends AbstractStringInstanceIdentifierCodec {
+    private final RandomPrefix prefixes = new RandomPrefix();
+    private final DataSchemaContextTree schemaTree;
+
+
+    RandomPrefixInstanceIdentifierSerializer(SchemaContext ctx) {
+        schemaTree = DataSchemaContextTree.from(ctx);
+    }
+
+    Iterable<Map.Entry<URI, String>> getPrefixes() {
+        return prefixes.getPrefixes();
+    }
+
+    @Override
+    protected String prefixForNamespace(final URI namespace) {
+        return prefixes.encodePrefix(namespace);
+    }
+
+    @Override
+    protected QName createQName(final String prefix, final String localName) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    protected DataSchemaContextTree getDataContextTree() {
+        return schemaTree;
+    }
+
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtils.java
new file mode 100644 (file)
index 0000000..0ed20c4
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import com.google.common.annotations.Beta;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import java.net.URI;
+import java.util.Map.Entry;
+import javax.annotation.Nonnull;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaNode;
+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.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for bridging JAXP Stream and YANG Data APIs. Note that the definition of this class by no means final
+ * and subject to change as more functionality is centralized here.
+ */
+@Beta
+public class XmlStreamUtils {
+    private static final Logger LOG = LoggerFactory.getLogger(XmlStreamUtils.class);
+    private final XmlCodecProvider codecProvider;
+    private final Optional<SchemaContext> schemaContext;
+
+    protected XmlStreamUtils(final XmlCodecProvider codecProvider) {
+        this(codecProvider, null);
+    }
+
+    private XmlStreamUtils(final XmlCodecProvider codecProvider, final SchemaContext schemaContext) {
+        this.codecProvider = Preconditions.checkNotNull(codecProvider);
+        this.schemaContext = Optional.fromNullable(schemaContext);
+    }
+
+    /**
+     * Create a new instance encapsulating a particular codec provider.
+     *
+     * @param codecProvider
+     *            XML codec provider
+     * @return A new instance
+     */
+    public static XmlStreamUtils create(final XmlCodecProvider codecProvider) {
+        return new XmlStreamUtils(codecProvider);
+    }
+
+    /**
+     * Write an InstanceIdentifier into the output stream. Calling corresponding
+     * {@link XMLStreamWriter#writeStartElement(String)} and {@link XMLStreamWriter#writeEndElement()} is the
+     * responsibility of the caller.
+     *
+     * @param writer
+     *            XML Stream writer
+     * @param id
+     *            InstanceIdentifier
+     * @throws XMLStreamException
+     *
+     * @deprecated Use {@link #writeInstanceIdentifier(XMLStreamWriter, YangInstanceIdentifier)} instead.
+     */
+    @Deprecated
+    public static void write(@Nonnull final XMLStreamWriter writer, @Nonnull final YangInstanceIdentifier id)
+            throws XMLStreamException {
+        Preconditions.checkNotNull(writer, "Writer may not be null");
+        Preconditions.checkNotNull(id, "Variable should contain instance of instance identifier and can't be null");
+
+        final RandomPrefix prefixes = new RandomPrefix();
+        final String str = XmlUtils.encodeIdentifier(prefixes, id);
+        writeNamespaceDeclarations(writer, prefixes.getPrefixes());
+        writer.writeCharacters(str);
+    }
+
+    @VisibleForTesting
+    static void writeAttribute(final XMLStreamWriter writer, final Entry<QName, String> attribute,
+            final RandomPrefix randomPrefix) throws XMLStreamException {
+        final QName key = attribute.getKey();
+        final String prefix = randomPrefix.encodePrefix(key.getNamespace());
+        writer.writeAttribute("xmlns:" + prefix, key.getNamespace().toString());
+        writer.writeAttribute(prefix, key.getNamespace().toString(), key.getLocalName(), attribute.getValue());
+    }
+
+    /**
+     * Write a value into a XML stream writer. This method assumes the start and end of element is emitted by the
+     * caller.
+     *
+     * @param writer
+     *            XML Stream writer
+     * @param schemaNode
+     *            Schema node that describes the value
+     * @param value
+     *            data value
+     * @param parent
+     *            optional parameter of a module QName owning the leaf definition
+     * @throws XMLStreamException
+     *             if an encoding problem occurs
+     */
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+            final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+        if (value == null) {
+            LOG.debug("Value of {}:{} is null, not encoding it", schemaNode.getQName().getNamespace(), schemaNode
+                    .getQName().getLocalName());
+            return;
+        }
+
+        Preconditions
+                .checkArgument(
+                        schemaNode instanceof LeafSchemaNode || schemaNode instanceof LeafListSchemaNode,
+                        "Unable to write value for node %s, only nodes of type: leaf and leaf-list can be written at this point",
+                        schemaNode.getQName());
+
+        TypeDefinition<?> type = schemaNode instanceof LeafSchemaNode ? ((LeafSchemaNode) schemaNode).getType()
+                : ((LeafListSchemaNode) schemaNode).getType();
+
+        TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+
+        if (schemaContext.isPresent() && baseType instanceof LeafrefTypeDefinition) {
+            LeafrefTypeDefinition leafrefTypeDefinition = (LeafrefTypeDefinition) baseType;
+            baseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypeDefinition, schemaContext.get(), schemaNode);
+        }
+
+        writeValue(writer, baseType, value, parent);
+    }
+
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+            final Object value) throws XMLStreamException {
+        writeValue(writer, schemaNode, value, Optional.<QNameModule> absent());
+    }
+
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final SchemaNode schemaNode,
+            final Object value, final QNameModule parent) throws XMLStreamException {
+        writeValue(writer, schemaNode, value, Optional.of(parent));
+    }
+
+    /**
+     * Write a value into a XML stream writer. This method assumes the start and end of element is emitted by the
+     * caller.
+     *
+     * @param writer
+     *            XML Stream writer
+     * @param type
+     *            data type. In case of leaf ref this should be the type of leaf being referenced
+     * @param value
+     *            data value
+     * @param parent
+     *            optional parameter of a module QName owning the leaf definition
+     * @throws XMLStreamException
+     *             if an encoding problem occurs
+     */
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+            final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+        if (value == null) {
+            LOG.debug("Value of {}:{} is null, not encoding it", type.getQName().getNamespace(), type.getQName()
+                    .getLocalName());
+            return;
+        }
+        TypeDefinition<?> baseType = XmlUtils.resolveBaseTypeFrom(type);
+
+        if (baseType instanceof IdentityrefTypeDefinition) {
+            if (parent.isPresent()) {
+                write(writer, (IdentityrefTypeDefinition) baseType, value, parent);
+            } else {
+                write(writer, (IdentityrefTypeDefinition) baseType, value, Optional.<QNameModule> absent());
+            }
+        } else if (baseType instanceof InstanceIdentifierTypeDefinition) {
+            write(writer, (InstanceIdentifierTypeDefinition) baseType, value);
+        } else {
+            final TypeDefinitionAwareCodec<Object, ?> codec = codecProvider.codecFor(type);
+            String text;
+            if (codec != null) {
+                try {
+                    text = codec.serialize(value);
+                } catch (ClassCastException e) {
+                    LOG.error("Provided node value {} did not have type {} required by mapping. Using stream instead.",
+                            value, type, e);
+                    text = String.valueOf(value);
+                }
+            } else {
+                LOG.error("Failed to find codec for {}, falling back to using stream", type);
+                text = String.valueOf(value);
+            }
+            writer.writeCharacters(text);
+        }
+    }
+
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+            final Object value, final QNameModule parent) throws XMLStreamException {
+        writeValue(writer, type, value, Optional.of(parent));
+    }
+
+    public void writeValue(@Nonnull final XMLStreamWriter writer, @Nonnull final TypeDefinition<?> type,
+            final Object value) throws XMLStreamException {
+        writeValue(writer, type, value, Optional.<QNameModule> absent());
+    }
+
+    @VisibleForTesting
+    static void write(@Nonnull final XMLStreamWriter writer, @Nonnull final IdentityrefTypeDefinition type,
+            @Nonnull final Object value, final Optional<QNameModule> parent) throws XMLStreamException {
+        if (value instanceof QName) {
+            final QName qname = (QName) value;
+            final String prefix = "x";
+
+            // in case parent is present and same as element namespace write value without namespace
+            if (parent.isPresent() && qname.getNamespace().equals(parent.get().getNamespace())) {
+                writer.writeCharacters(qname.getLocalName());
+            } else {
+                final String ns = qname.getNamespace().toString();
+                writer.writeNamespace(prefix, ns);
+                writer.writeCharacters(prefix + ':' + qname.getLocalName());
+            }
+
+        } else {
+            LOG.debug("Value of {}:{} is not a QName but {}", type.getQName().getNamespace(), type.getQName()
+                    .getLocalName(), value.getClass());
+            writer.writeCharacters(String.valueOf(value));
+        }
+    }
+
+    private void write(@Nonnull final XMLStreamWriter writer, @Nonnull final InstanceIdentifierTypeDefinition type,
+            @Nonnull final Object value) throws XMLStreamException {
+        if (value instanceof YangInstanceIdentifier) {
+            writeInstanceIdentifier(writer, (YangInstanceIdentifier) value);
+        } else {
+            LOG.warn("Value of {}:{} is not an InstanceIdentifier but {}", type.getQName().getNamespace(), type
+                    .getQName().getLocalName(), value.getClass());
+            writer.writeCharacters(String.valueOf(value));
+        }
+    }
+
+    public void writeInstanceIdentifier(XMLStreamWriter writer, YangInstanceIdentifier value) throws XMLStreamException {
+        if (schemaContext.isPresent()) {
+            RandomPrefixInstanceIdentifierSerializer iiCodec = new RandomPrefixInstanceIdentifierSerializer(
+                    schemaContext.get());
+            String serializedValue = iiCodec.serialize(value);
+            writeNamespaceDeclarations(writer, iiCodec.getPrefixes());
+            writer.writeCharacters(serializedValue);
+        } else {
+            LOG.warn("Schema context not present in {}, serializing {} without schema.", this, value);
+            write(writer, value);
+        }
+    }
+
+    private static void writeNamespaceDeclarations(XMLStreamWriter writer, Iterable<Entry<URI, String>> prefixes)
+            throws XMLStreamException {
+        for (Entry<URI, String> e : prefixes) {
+            final String ns = e.getKey().toString();
+            final String p = e.getValue();
+            writer.writeNamespace(p, ns);
+        }
+    }
+
+    public static XmlStreamUtils create(final XmlCodecProvider codecProvider, final SchemaContext schemaContext) {
+        return new XmlStreamUtils(codecProvider, schemaContext);
+    }
+}
diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlUtils.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlUtils.java
new file mode 100644 (file)
index 0000000..9d6fe8d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+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;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlCodecProvider;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * Common XML-related utility methods, which are not specific to a particular JAXP API.
+ */
+public final class XmlUtils {
+    public static final XmlCodecProvider DEFAULT_XML_CODEC_PROVIDER = new XmlCodecProvider() {
+        @Override
+        public TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> codecFor(final TypeDefinition<?> baseType) {
+            return TypeDefinitionAwareCodec.from(baseType);
+        }
+    };
+
+    private XmlUtils() {
+    }
+
+    public static TypeDefinition<?> resolveBaseTypeFrom(final @Nonnull TypeDefinition<?> type) {
+        TypeDefinition<?> superType = type;
+        while (superType.getBaseType() != null) {
+            superType = superType.getBaseType();
+        }
+        return superType;
+    }
+
+    /**
+     *
+     * @deprecated Use {@link RandomPrefixInstanceIdentifierSerializer} instead.
+     */
+    @Deprecated
+    static String encodeIdentifier(final RandomPrefix prefixes, final YangInstanceIdentifier id) {
+        StringBuilder textContent = new StringBuilder();
+        for (PathArgument pathArgument : id.getPathArguments()) {
+            textContent.append('/');
+
+            final QName nt = pathArgument.getNodeType();
+            textContent.append(prefixes.encodePrefix(nt.getNamespace()));
+            textContent.append(':');
+            textContent.append(nt.getLocalName());
+
+            if (pathArgument instanceof NodeIdentifierWithPredicates) {
+                Map<QName, Object> predicates = ((NodeIdentifierWithPredicates) pathArgument).getKeyValues();
+
+                for (Map.Entry<QName, Object> entry : predicates.entrySet()) {
+                    final QName key = entry.getKey();
+                    textContent.append('[');
+                    textContent.append(prefixes.encodePrefix(key.getNamespace()));
+                    textContent.append(':');
+                    textContent.append(key.getLocalName());
+                    textContent.append("='");
+                    textContent.append(String.valueOf(entry.getValue()));
+                    textContent.append("']");
+                }
+            } else if (pathArgument instanceof NodeWithValue) {
+                textContent.append("[.='");
+                textContent.append(((NodeWithValue) pathArgument).getValue());
+                textContent.append("']");
+            }
+        }
+
+        return textContent.toString();
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/RetestUtils.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/RetestUtils.java
new file mode 100644 (file)
index 0000000..9e68b4e
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.util.Collection;
+import java.util.List;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class RetestUtils {
+
+    private RetestUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static SchemaContext parseYangSources(StatementStreamSource... sources) throws SourceException,
+            ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        reactor.addSources(sources);
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(File... files) throws SourceException, ReactorException,
+            FileNotFoundException {
+
+        StatementStreamSource[] sources = new StatementStreamSource[files.length];
+
+        for (int i = 0; i < files.length; i++) {
+            sources[i] = new YangStatementSourceImpl(new FileInputStream(files[i]));
+        }
+
+        return parseYangSources(sources);
+    }
+
+    public static SchemaContext parseYangSources(Collection<File> files) throws SourceException, ReactorException,
+            FileNotFoundException {
+        return parseYangSources(files.toArray(new File[files.size()]));
+    }
+
+    public static SchemaContext parseYangStreams(List<InputStream> streams)
+            throws SourceException, ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                .newBuild();
+        return reactor.buildEffective(streams);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/Bug2964Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/Bug2964Test.java
new file mode 100644 (file)
index 0000000..329ac4e
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import com.google.common.base.Charsets;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+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.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+public class Bug2964Test {
+
+    public static final String XML_CONTENT = "<cont2 xmlns=\"urn:opendaylight:yangtools:leafref:test\">\n"
+            + "<point-to-identityrefleaf>test-identity</point-to-identityrefleaf>\n" + "</cont2>";
+
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    private static final String NAMESPACE = "urn:opendaylight:yangtools:leafref:test";
+    private static final String TEST_IDENTITY = "test-identity";
+    private static final String CONT_2 = "cont2";
+    private static final String IDENTITY_LEAFREF = "point-to-identityrefleaf";
+
+    static {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws Exception {
+        File leafRefTestYang = new File(getClass().getResource("/leafref-test.yang").toURI());
+        schema = RetestUtils.parseYangSources(leafRefTestYang);
+    }
+
+    public static Document readXmlToDocument(final String xmlContent) throws SAXException, IOException {
+        return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8)));
+    }
+
+    @Test
+    public void testLeafrefIdentityRefDeserialization() throws Exception {
+        final URI namespaceUri = new URI(NAMESPACE);
+
+        final Document document = readXmlToDocument(XML_CONTENT);
+        final Element identityLeafRefElement = (Element) document.getDocumentElement().getFirstChild().getNextSibling();
+
+        final Module leafrefModule = schema.findModuleByNamespaceAndRevision(namespaceUri, null);
+        final ContainerSchemaNode cont2 = (ContainerSchemaNode) leafrefModule.getDataChildByName(CONT_2);
+        final DataSchemaNode identityLeafRefSchema = cont2.getDataChildByName(IDENTITY_LEAFREF);
+        final Object parsedValue = DomUtils.parseXmlValue(identityLeafRefElement, DomUtils.defaultValueCodecProvider(),
+                identityLeafRefSchema, ((LeafSchemaNode) identityLeafRefSchema).getType(), schema);
+
+        assertThat(parsedValue, instanceOf(QName.class));
+        final QName parsedQName = (QName) parsedValue;
+        assertEquals(namespaceUri, parsedQName.getNamespace());
+        assertEquals(TEST_IDENTITY, parsedQName.getLocalName());
+    }
+
+    public static Document readXmlToDocument(final InputStream xmlContent) throws SAXException, IOException {
+        final DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDERFACTORY.newDocumentBuilder();
+        } catch (final ParserConfigurationException e) {
+            throw new IllegalStateException("Failed to parse XML document", e);
+        }
+        final Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/RandomPrefixTest.java
new file mode 100644 (file)
index 0000000..257a954
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.net.URI;
+import java.util.Date;
+import java.util.List;
+import org.hamcrest.CoreMatchers;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+
+public class RandomPrefixTest {
+    static final int MAX_COUNTER = 4000;
+
+    @Test
+    public void testEncodeDecode() throws Exception {
+
+        final List<String> allGenerated = Lists.newArrayList();
+        for (int i = 0; i < MAX_COUNTER; i++) {
+            final String encoded = RandomPrefix.encode(i);
+            assertEquals(RandomPrefix.decode(encoded), i);
+            allGenerated.add(encoded);
+        }
+
+        assertEquals(allGenerated.size(), MAX_COUNTER);
+        assertEquals("dPT", allGenerated.get(MAX_COUNTER - 1));
+        assertEquals("a", allGenerated.get(0));
+        assertEquals(allGenerated.size(), Sets.newHashSet(allGenerated).size());
+    }
+
+    @Test
+    public void testQNameWithPrefix() throws Exception {
+        final RandomPrefix a = new RandomPrefix();
+
+        final List<String> allGenerated = Lists.newArrayList();
+        for (int i = 0; i < MAX_COUNTER; i++) {
+            final String prefix = RandomPrefix.encode(i);
+            final URI uri = new URI("localhost:" + prefix);
+            final QName qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+            allGenerated.add(a.encodePrefix(qName.getNamespace()));
+        }
+
+        assertEquals(MAX_COUNTER, allGenerated.size());
+        // We are generating MAX_COUNTER_VALUE + 27 prefixes total, so we should encounter a reset in prefix a start
+        // from 0 at some point
+        // At the end, there should be only 27 values in RandomPrefix cache
+        assertEquals(MAX_COUNTER, Iterables.size(a.getPrefixes()));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xml")));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmla")));
+        assertThat(allGenerated, CoreMatchers.not(CoreMatchers.hasItem("xmlz")));
+
+        assertEquals(1, Iterables.frequency(allGenerated, "a"));
+    }
+
+    @Test
+    public void test2QNames1Namespace() throws Exception {
+        final RandomPrefix a = new RandomPrefix();
+
+        final URI uri = URI.create("localhost");
+        final QName qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+        final QName qName2 = QName.create(QNameModule.create(uri, new Date()), "local-name");
+
+        assertEquals(a.encodePrefix(qName.getNamespace()), a.encodePrefix(qName2.getNamespace()));
+    }
+
+    @Test
+    public void testQNameNoPrefix() throws Exception {
+        final RandomPrefix a = new RandomPrefix();
+
+        final URI uri = URI.create("localhost");
+        QName qName = QName.create(uri, new Date(), "local-name");
+        assertEquals("a", a.encodePrefix(qName.getNamespace()));
+        qName = QName.create(QNameModule.create(uri, new Date()), "local-name");
+        assertEquals("a", a.encodePrefix(qName.getNamespace()));
+        qName = QName.create(QNameModule.create(URI.create("second"), new Date()), "local-name");
+        assertEquals("b", a.encodePrefix(qName.getNamespace()));
+
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlDocumentUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlDocumentUtilsTest.java
new file mode 100644 (file)
index 0000000..319b4bc
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import com.google.common.base.Charsets;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import org.junit.Before;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+public class XmlDocumentUtilsTest {
+
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    static {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    public static final String XML_CONTENT = "<input xmlns=\"urn:opendaylight:controller:rpc:test\">\n"
+            + "<a>value</a>\n" + "<ref xmlns:ltha=\"urn:opendaylight:controller:rpc:test\">"
+            + "/ltha:cont/ltha:l[  ltha:id='id/foo/bar'  ]" + "</ref>\n" + "</input>";
+
+    public static final String RPC_REPLY = "<rpc-reply xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\" message-id=\"m-1\">\n"
+            + " <ok/>\n" + "</rpc-reply>";
+
+    private SchemaContext schema;
+    private RpcDefinition testRpc;
+
+    @Before
+    public void setUp() throws Exception {
+        File rpcTestYang = new File(getClass().getResource("/rpc-test.yang").toURI());
+        schema = RetestUtils.parseYangSources(rpcTestYang);
+        final Module rpcTestModule = schema.getModules().iterator().next();
+        testRpc = rpcTestModule.getRpcs().iterator().next();
+    }
+
+    public static Document readXmlToDocument(final String xmlContent) throws SAXException, IOException {
+        return readXmlToDocument(new ByteArrayInputStream(xmlContent.getBytes(Charsets.UTF_8)));
+    }
+
+    public static Document readXmlToDocument(final InputStream xmlContent) throws SAXException, IOException {
+        final DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDERFACTORY.newDocumentBuilder();
+        } catch (final ParserConfigurationException e) {
+            throw new IllegalStateException("Failed to parse XML document", e);
+        }
+        final Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtilsTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/codec/xml/retest/XmlStreamUtilsTest.java
new file mode 100644 (file)
index 0000000..b4fbd54
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.codec.xml.retest;
+
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.AbstractMap;
+import java.util.Date;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamWriter;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+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.LeafrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
+import org.opendaylight.yangtools.yang.model.util.StringType;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.w3c.dom.Document;
+
+public class XmlStreamUtilsTest {
+
+    public static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newFactory();
+
+    private static SchemaContext schemaContext;
+    private static Module leafRefModule;
+
+    @BeforeClass
+    public static void initialize() throws URISyntaxException, FileNotFoundException, ReactorException {
+        final File file = new File(XmlStreamUtils.class.getResource("/leafref-test.yang").toURI());
+        schemaContext = RetestUtils.parseYangSources(file);
+        assertNotNull(schemaContext);
+        assertEquals(1, schemaContext.getModules().size());
+        leafRefModule = schemaContext.getModules().iterator().next();
+        assertNotNull(leafRefModule);
+    }
+
+    @Test
+    public void testWriteAttribute() throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+        writer.writeStartElement("element");
+
+        QName name = getAttrQName("namespace", "2012-12-12", "attr", Optional.of("prefix"));
+        final Map.Entry<QName, String> attributeEntry = new AbstractMap.SimpleEntry<>(name, "value");
+
+        name = getAttrQName("namespace2", "2012-12-12", "attr", Optional.<String> absent());
+        final Map.Entry<QName, String> attributeEntryNoPrefix = new AbstractMap.SimpleEntry<>(name, "value");
+
+        final RandomPrefix randomPrefix = new RandomPrefix();
+        XmlStreamUtils.writeAttribute(writer, attributeEntry, randomPrefix);
+        XmlStreamUtils.writeAttribute(writer, attributeEntryNoPrefix, randomPrefix);
+
+        writer.writeEndElement();
+        writer.close();
+        out.close();
+
+        final String xmlAsString = new String(out.toByteArray());
+
+        final Map<String, String> mappedPrefixes = mapPrefixed(randomPrefix.getPrefixes());
+        assertEquals(2, mappedPrefixes.size());
+        final String randomPrefixValue = mappedPrefixes.get("namespace2");
+
+        final String expectedXmlAsString = "<element xmlns:a=\"namespace\" a:attr=\"value\" xmlns:" + randomPrefixValue
+                + "=\"namespace2\" " + randomPrefixValue + ":attr=\"value\"></element>";
+
+        XMLUnit.setIgnoreAttributeOrder(true);
+        final Document control = XMLUnit.buildControlDocument(expectedXmlAsString);
+        final Document test = XMLUnit.buildTestDocument(xmlAsString);
+        final Diff diff = XMLUnit.compareXML(control, test);
+
+        final boolean identical = diff.identical();
+        assertTrue("Xml differs: " + diff.toString(), identical);
+    }
+
+    @Test
+    public void testWriteIdentityRef() throws Exception {
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final XMLStreamWriter writer = XML_OUTPUT_FACTORY.createXMLStreamWriter(out);
+
+        writer.writeStartElement("element");
+        final QNameModule parent = QNameModule.create(URI.create("parent:uri"), new Date());
+        XmlStreamUtils.write(writer, null, QName.create(parent, "identity"), Optional.of(parent));
+        writer.writeEndElement();
+
+        writer.writeStartElement("elementDifferent");
+        XmlStreamUtils.write(writer, null, QName.create("different:namespace", "identity"), Optional.of(parent));
+        writer.writeEndElement();
+
+        writer.close();
+        out.close();
+
+        final String xmlAsString = new String(out.toByteArray()).replaceAll("\\s*", "");
+        assertThat(xmlAsString, containsString("element>identity"));
+
+        final Pattern prefixedIdentityPattern = Pattern.compile(".*\"different:namespace\">(.*):identity.*");
+        final Matcher matcher = prefixedIdentityPattern.matcher(xmlAsString);
+        assertTrue("Xml: " + xmlAsString + " should match: " + prefixedIdentityPattern, matcher.matches());
+    }
+
+    /**
+     * One leafref reference to other leafref via relative references
+     */
+    @Test
+    public void testLeafRefRelativeChaining() {
+        getTargetNodeForLeafRef("leafname3", StringType.class);
+    }
+
+    @Test
+    public void testLeafRefRelative() {
+        getTargetNodeForLeafRef("pointToStringLeaf", StringType.class);
+    }
+
+    @Test
+    public void testLeafRefAbsoluteWithSameTarget() {
+        getTargetNodeForLeafRef("absname", InstanceIdentifierType.class);
+    }
+
+    /**
+     * Tests relative path with double point inside path (e. g. "../../lf:interface/../lf:cont2/lf:stringleaf")
+     */
+    @Ignore
+    // ignored because this isn't implemented
+    @Test
+    public void testLeafRefWithDoublePointInPath() {
+        getTargetNodeForLeafRef("lf-with-double-point-inside", StringType.class);
+    }
+
+    @Test
+    public void testLeafRefRelativeAndAbsoluteWithSameTarget() {
+        final TypeDefinition<?> targetNodeForAbsname = getTargetNodeForLeafRef("absname", InstanceIdentifierType.class);
+        final TypeDefinition<?> targetNodeForRelname = getTargetNodeForLeafRef("relname", InstanceIdentifierType.class);
+        assertEquals(targetNodeForAbsname, targetNodeForRelname);
+    }
+
+    private TypeDefinition<?> getTargetNodeForLeafRef(final String nodeName, final Class<?> clas) {
+        final LeafSchemaNode schemaNode = findSchemaNodeWithLeafrefType(leafRefModule, nodeName);
+        assertNotNull(schemaNode);
+        final LeafrefTypeDefinition leafrefTypedef = findLeafrefType(schemaNode);
+        assertNotNull(leafrefTypedef);
+        final TypeDefinition<?> targetBaseType = SchemaContextUtil.getBaseTypeForLeafRef(leafrefTypedef, schemaContext,
+                schemaNode);
+        assertEquals("Wrong class found.", clas, targetBaseType.getClass());
+        return targetBaseType;
+    }
+
+    private static Map<String, String> mapPrefixed(final Iterable<Map.Entry<URI, String>> prefixes) {
+        final Map<String, String> mappedPrefixes = Maps.newHashMap();
+        for (final Map.Entry<URI, String> prefix : prefixes) {
+            mappedPrefixes.put(prefix.getKey().toString(), prefix.getValue());
+        }
+        return mappedPrefixes;
+    }
+
+    private static QName getAttrQName(final String namespace, final String revision, final String localName,
+            final Optional<String> prefix) {
+        if (prefix.isPresent()) {
+            final QName moduleQName = QName.create(namespace, revision, "module");
+            final QNameModule module = QNameModule.create(moduleQName.getNamespace(), moduleQName.getRevision());
+            return QName.create(module, localName);
+        } else {
+            return QName.create(namespace, revision, localName);
+        }
+    }
+
+    private LeafSchemaNode findSchemaNodeWithLeafrefType(final DataNodeContainer module, final String nodeName) {
+        for (final DataSchemaNode childNode : module.getChildNodes()) {
+            if (childNode instanceof DataNodeContainer) {
+                LeafSchemaNode leafrefFromRecursion = findSchemaNodeWithLeafrefType((DataNodeContainer) childNode,
+                        nodeName);
+                if (leafrefFromRecursion != null) {
+                    return leafrefFromRecursion;
+                }
+            } else if (childNode.getQName().getLocalName().equals(nodeName) && childNode instanceof LeafSchemaNode) {
+                final TypeDefinition<?> leafSchemaNodeType = ((LeafSchemaNode) childNode).getType();
+                if (leafSchemaNodeType instanceof LeafrefTypeDefinition) {
+                    return (LeafSchemaNode) childNode;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static LeafrefTypeDefinition findLeafrefType(final LeafSchemaNode schemaNode) {
+        final TypeDefinition<?> type = schemaNode.getType();
+        if (type instanceof LeafrefTypeDefinition) {
+            return (LeafrefTypeDefinition) type;
+        }
+        return null;
+    }
+}
index f73c1d287bd596fe502d1ab3af72c03de35f6b84..8a613fccd43a1467d3bb443c727daa9684f4a6ce 100644 (file)
@@ -108,6 +108,7 @@ public class DataTreeCandidateValidatorTest2 {
                 .takeSnapshot().newModification();
         writeModification.write(devicesPath, devicesContainer);
 
+        writeModification.ready();
         final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree
                 .prepare(writeModification);
 
@@ -197,6 +198,7 @@ public class DataTreeCandidateValidatorTest2 {
         final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
         initialDataTreeModification.write(path2, deviceTypesContainer);
 
+        initialDataTreeModification.ready();
         final DataTreeCandidate writeChipsCandidate = inMemoryDataTree
                 .prepare(initialDataTreeModification);
 
index 1d9ad774f58adc1e81dbbaa729bf396b7c4d63ad..86d75fe72cc900c5c46de01bbe6328d34a86ea37 100644 (file)
@@ -113,6 +113,7 @@ public class DataTreeCandidateValidatorTest3 {
                 .takeSnapshot().newModification();
         writeModification.write(devicesPath, devicesContainer);
 
+        writeModification.ready();
         final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree
                 .prepare(writeModification);
 
@@ -153,6 +154,7 @@ public class DataTreeCandidateValidatorTest3 {
         mergeModification.write(devicesPath, devicesContainer);
         mergeModification.merge(devicesPath, devicesContainer);
 
+        mergeModification.ready();
         final DataTreeCandidate mergeDevicesCandidate = inMemoryDataTree
                 .prepare(mergeModification);
 
@@ -246,6 +248,7 @@ public class DataTreeCandidateValidatorTest3 {
         final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
         initialDataTreeModification.write(path2, deviceTypesContainer);
 
+        initialDataTreeModification.ready();
         final DataTreeCandidate writeChipsCandidate = inMemoryDataTree
                 .prepare(initialDataTreeModification);
 
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest.java
new file mode 100644 (file)
index 0000000..3ba97df
--- /dev/null
@@ -0,0 +1,833 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+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.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+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.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidatation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest {
+
+    private static SchemaContext context;
+    private static Module valModule;
+    private static QNameModule valModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static TipProducingDataTree inMemoryDataTree;
+
+    private static QName odl;
+    private static QName project;
+    private static QName name;
+    private static QName desc;
+    private static QName lead;
+    private static QName owner;
+    private static QName odlContributor;
+    private static QName contributor;
+    private static QName odlProjectName;
+    private static QName odlProjectDesc;
+    private static QName login;
+    private static QName contributorName;
+    private static QName l1;
+    private static QName l2;
+    private static QName con1;
+    private static QName ch1;
+    private static QName ch2;
+    private static QName leafrefInChoice;
+    private static QName listInChoice;
+
+    private static QName leafrefInChoiceToChoice;
+    private static QName con3;
+    private static QName list3InChoice;
+    private static QName l3;
+    private static QName choiceInCon3;
+
+    private static QName listInChoiceKey;
+    private static QName k;
+
+    private static QName leafrefLeafList;
+
+    private static final Logger LOG = LoggerFactory.getLogger("");
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws FileNotFoundException, ReactorException,
+            URISyntaxException {
+        initSchemaContext();
+
+        initLeafRefContext();
+
+        initQnames();
+
+        initDataTree();
+
+    }
+
+    private static void initSchemaContext() throws URISyntaxException,
+            FileNotFoundException, ReactorException {
+        final File resourceFile = new File(DataTreeCandidateValidatorTest.class
+                .getResource("/leafref-validation/leafref-validation.yang")
+                .toURI());
+        final File resourceDir = resourceFile.getParentFile();
+
+        context = RetestUtils.parseYangSources(Arrays.asList(resourceDir
+                .listFiles()));
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation")) {
+                valModule = module;
+            }
+        }
+
+        valModuleQname = valModule.getQNameModule();
+    }
+
+    private static void initLeafRefContext() {
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    private static void initQnames() {
+        odl = QName.create(valModuleQname, "odl-project");
+        project = QName.create(valModuleQname, "project");
+        name = QName.create(valModuleQname, "name");
+        desc = QName.create(valModuleQname, "desc");
+        lead = QName.create(valModuleQname, "project-lead");
+        owner = QName.create(valModuleQname, "project-owner");
+
+        odlContributor = QName.create(valModuleQname, "odl-contributor");
+        contributor = QName.create(valModuleQname, "contributor");
+        odlProjectName = QName.create(valModuleQname, "odl-project-name");
+        login = QName.create(valModuleQname, "login");
+        contributorName = QName.create(valModuleQname, "contributor-name");
+
+        con1 = QName.create(valModuleQname, "con1");
+        l1 = QName.create(valModuleQname, "l1");
+        l2 = QName.create(valModuleQname, "l2");
+        odlProjectDesc = QName.create(valModuleQname, "odl-project-desc");
+
+        ch1 = QName.create(valModuleQname, "ch1");
+        ch2 = QName.create(valModuleQname, "ch2");
+        leafrefInChoice = QName.create(valModuleQname, "leafref-in-choice");
+        listInChoice = QName.create(valModuleQname, "list-in-choice");
+
+        leafrefInChoiceToChoice = QName.create(valModuleQname,
+                "leafref-in-choice-to-choice");
+        con3 = QName.create(valModuleQname, "con3");
+        list3InChoice = QName.create(valModuleQname, "list3-in-choice");
+        l3 = QName.create(valModuleQname, "l3");
+        choiceInCon3 = QName.create(valModuleQname, "choice-in-con3");
+
+        listInChoiceKey = QName.create(valModuleQname, "list-in-choice-key");
+        k = QName.create(valModuleQname, "k");
+
+        leafrefLeafList = QName.create(valModuleQname, "leafref-leaf-list");
+
+    }
+
+    private static void initDataTree() {
+        inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(context);
+
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+
+        final ContainerSchemaNode odlProjContSchemaNode = (ContainerSchemaNode) valModule
+                .getDataChildByName(odl);
+
+        final ContainerNode odlProjectContainer = createOdlContainer(odlProjContSchemaNode);
+
+        final YangInstanceIdentifier path = YangInstanceIdentifier.of(odl);
+        initialDataTreeModification.write(path, odlProjectContainer);
+        initialDataTreeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(initialDataTreeModification);
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest() {
+        write();
+
+        write2();
+
+        delete();
+
+        writeContributors();
+
+        writeMapEntry();
+
+        writeIntoMapEntry();
+    }
+
+    private static void writeContributors() {
+
+        final ContainerSchemaNode contributorContSchemaNode = (ContainerSchemaNode) valModule
+                .getDataChildByName(odlContributor);
+
+        final ContainerNode contributorContainer = createBasicContributorContainer(contributorContSchemaNode);
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(contributorPath, contributorContainer);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write of contributors: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(3, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write of contributors: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void writeIntoMapEntry() {
+
+        final Map<QName, Object> keys = new HashMap<QName, Object>();
+        keys.put(name, "New Project");
+        final NodeIdentifierWithPredicates mapEntryPath = new NodeIdentifierWithPredicates(
+                project, keys);
+
+        final YangInstanceIdentifier leaderPath = YangInstanceIdentifier
+                .of(odl).node(project).node(mapEntryPath).node(lead);
+
+        final LeafNode<String> leader = ImmutableNodes.leafNode(lead,
+                "Updated leader");
+
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(leaderPath, leader);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write into map entry (update of leader name): ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(1, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write into map entry (update of leader name): ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void writeMapEntry() {
+
+        final Map<QName, Object> keys = new HashMap<QName, Object>();
+        keys.put(name, "New Project");
+        final NodeIdentifierWithPredicates mapEntryPath = new NodeIdentifierWithPredicates(
+                project, keys);
+
+        final YangInstanceIdentifier newOdlProjectMapEntryPath = YangInstanceIdentifier
+                .of(odl).node(project).node(mapEntryPath);
+
+        final ContainerSchemaNode odlProjContSchemaNode = (ContainerSchemaNode) valModule
+                .getDataChildByName(odl);
+        final ListSchemaNode projListSchemaNode = (ListSchemaNode) odlProjContSchemaNode
+                .getDataChildByName(project);
+        final MapEntryNode newProjectMapEntry = createProjectListEntry(
+                "New Project", "New Project description ...",
+                "Leader of New Project", "Owner of New Project",
+                projListSchemaNode);
+
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(newOdlProjectMapEntryPath, newProjectMapEntry);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before map entry write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(2, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After map entry write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+
+    }
+
+    private static void write() {
+
+        final ContainerSchemaNode contributorContSchemaNode = (ContainerSchemaNode) valModule
+                .getDataChildByName(odlContributor);
+
+        final ContainerNode contributorContainer = createContributorContainer(contributorContSchemaNode);
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(contributorPath, contributorContainer);
+
+        writeModification.write(YangInstanceIdentifier.of(l1),
+                ImmutableNodes.leafNode(l1, "Leafref l1 under the root"));
+        writeModification
+                .write(YangInstanceIdentifier.of(l2), ImmutableNodes.leafNode(
+                        l2, "Leafref target l2 under the root"));
+
+        writeModification.ready();
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(12, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+    }
+
+    private static void write2() {
+
+        final ContainerSchemaNode odlCon = (ContainerSchemaNode) valModule
+                .getDataChildByName(odl);
+        final ContainerSchemaNode con1Con = (ContainerSchemaNode) odlCon
+                .getDataChildByName(con1);
+        final LeafNode<String> l1Leaf = ImmutableNodes.leafNode(l1, "l1 value");
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+                .containerBuilder(con1Con);
+        containerBuilder.addChild(l1Leaf);
+        final ContainerNode con1Node = containerBuilder.build();
+
+        final YangInstanceIdentifier con1Path = YangInstanceIdentifier.of(odl)
+                .node(con1);
+        final DataTreeModification writeModification = inMemoryDataTree
+                .takeSnapshot().newModification();
+        writeModification.write(con1Path, con1Node);
+
+        final ChoiceNode choiceNode = createChoiceNode();
+        final YangInstanceIdentifier choicePath = YangInstanceIdentifier
+                .of(odl).node(ch1);
+        writeModification.write(choicePath, choiceNode);
+
+        final ContainerNode con3Node = createCon3Node();
+        final YangInstanceIdentifier con3Path = YangInstanceIdentifier.of(odl)
+                .node(con3);
+        writeModification.write(con3Path, con3Node);
+
+        final LeafSetNode<?> leafListNode = createLeafRefLeafListNode();
+        final YangInstanceIdentifier leafListPath = YangInstanceIdentifier.of(
+                odl).node(leafrefLeafList);
+        writeModification.write(leafListPath, leafListNode);
+        writeModification.ready();
+
+        final DataTreeCandidate writeContributorsCandidate = inMemoryDataTree
+                .prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before write2: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeContributorsCandidate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeContributorsCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write2: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+    }
+
+    private static LeafSetNode<?> createLeafRefLeafListNode() {
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder = Builders
+                .leafSetBuilder();
+        leafSetBuilder.withNodeIdentifier(new NodeIdentifier(leafrefLeafList));
+
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k1"));
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k2"));
+        leafSetBuilder.addChild(createLeafSetEntry(leafrefLeafList, "k3"));
+
+        return leafSetBuilder.build();
+    }
+
+    private static ContainerNode createCon3Node() {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> mapBuilder = Builders
+                .mapBuilder();
+        mapBuilder.withNodeIdentifier(new NodeIdentifier(list3InChoice));
+
+        mapBuilder.addChild(createList3Entry("k1", "val1", "valA", "valX"));
+        mapBuilder.addChild(createList3Entry("k2", "val2", "valB", "valY"));
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder = Builders
+                .choiceBuilder();
+        choiceBuilder.withNodeIdentifier(new NodeIdentifier(choiceInCon3));
+
+        choiceBuilder.addChild(mapBuilder.build());
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> containerBuilder = Builders
+                .containerBuilder();
+        containerBuilder.withNodeIdentifier(new NodeIdentifier(con3));
+
+        containerBuilder.addChild(choiceBuilder.build());
+
+        return containerBuilder.build();
+    }
+
+    private static MapEntryNode createList3Entry(final String kVal,
+            final String l3Val1, final String l3Val2, final String l3Val3) {
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders
+                .mapEntryBuilder();
+        mapEntryBuilder.withNodeIdentifier(new NodeIdentifierWithPredicates(
+                list3InChoice, k, kVal));
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafSetBuilder = Builders
+                .leafSetBuilder();
+        leafSetBuilder.withNodeIdentifier(new NodeIdentifier(l3));
+
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val1));
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val2));
+        leafSetBuilder.addChild(createLeafSetEntry(l3, l3Val3));
+
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(k, kVal));
+        mapEntryBuilder.addChild(leafSetBuilder.build());
+
+        return mapEntryBuilder.build();
+    }
+
+    private static LeafSetEntryNode<Object> createLeafSetEntry(
+            final QName qname, final String val) {
+        final NormalizedNodeAttrBuilder<NodeWithValue, Object, LeafSetEntryNode<Object>> leafSetEntryBuilder = Builders
+                .leafSetEntryBuilder();
+        leafSetEntryBuilder.withNodeIdentifier(new NodeWithValue(qname, val));
+        leafSetEntryBuilder.withValue(val);
+        return leafSetEntryBuilder.build();
+    }
+
+    private static ChoiceNode createChoiceNode() {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listInChoiceBuilder = Builders
+                .mapBuilder();
+        listInChoiceBuilder
+                .withNodeIdentifier(new NodeIdentifier(listInChoice));
+
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key1",
+                "leafref-in-choice value", "val1"));
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key2",
+                "l1 value", "val2"));
+        listInChoiceBuilder.addChild(createListInChoiceEntry("key3",
+                "l1 value", "val3"));
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choice2Builder = Builders
+                .choiceBuilder();
+        choice2Builder.withNodeIdentifier(new NodeIdentifier(ch2));
+
+        choice2Builder.addChild(listInChoiceBuilder.build());
+
+        final DataContainerNodeBuilder<NodeIdentifier, ChoiceNode> choiceBuilder = Builders
+                .choiceBuilder();
+        choiceBuilder.withNodeIdentifier(new NodeIdentifier(ch1));
+        choiceBuilder.addChild(choice2Builder.build());
+
+        return choiceBuilder.build();
+    }
+
+    private static MapEntryNode createListInChoiceEntry(final String keyVal,
+            final String leafrefInChoiceVal,
+            final String leafrefInChoiceToChoiceVal) {
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> mapEntryBuilder = Builders
+                .mapEntryBuilder();
+
+        mapEntryBuilder.withNodeIdentifier(new NodeIdentifierWithPredicates(
+                listInChoice, listInChoiceKey, keyVal));
+
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(listInChoiceKey,
+                keyVal));
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(leafrefInChoice,
+                leafrefInChoiceVal));
+        mapEntryBuilder.addChild(ImmutableNodes.leafNode(
+                leafrefInChoiceToChoice, leafrefInChoiceToChoiceVal));
+
+        return mapEntryBuilder.build();
+    }
+
+    private static void delete() {
+
+        final YangInstanceIdentifier contributorPath = YangInstanceIdentifier
+                .of(odlContributor);
+        final DataTreeModification delete = inMemoryDataTree.takeSnapshot()
+                .newModification();
+        delete.delete(contributorPath);
+        delete.ready();
+
+        final DataTreeCandidate deleteContributorsCanditate = inMemoryDataTree
+                .prepare(delete);
+
+        LOG.debug("*************************");
+        LOG.debug("Before delete: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(deleteContributorsCanditate,
+                    rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(deleteContributorsCanditate);
+
+        LOG.debug("*************************");
+        LOG.debug("After delete: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+    }
+
+    private static ContainerNode createContributorContainer(
+            final ContainerSchemaNode contributorContSchemaNode) {
+
+        final ListSchemaNode contributorListSchemaNode = (ListSchemaNode) contributorContSchemaNode
+                .getDataChildByName(contributor);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> contributorContainerBldr = Builders
+                .containerBuilder(contributorContSchemaNode);
+
+        final MapNode contributorMap = createContributorList(contributorListSchemaNode);
+        contributorContainerBldr.addChild(contributorMap);
+
+        final ContainerNode contributorContainer = contributorContainerBldr
+                .build();
+
+        return contributorContainer;
+
+    }
+
+    private static MapNode createContributorList(
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> contributorMapBldr = Builders
+                .mapBuilder(contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry1 = createContributorListEntry(
+                "Leader of Yangtools", "Yangtools Leader name", "Yangtools",
+                "Yangtools description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry2 = createContributorListEntry(
+                "Leader of MD-SAL", "MD-SAL Leader name", "MD-SAL",
+                "MD-SAL description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry3 = createContributorListEntry(
+                "Leader of Controller", "Controller Leader name", "Controller",
+                "Controller description ...", contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry4 = createContributorListEntry(
+                "jdoe", "John Doe", "MD-SAL", "Yangtools description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry5 = createContributorListEntry(
+                "foo", "foo name", "Controller", "MD-SAL description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry6 = createContributorListEntry(
+                "bar", "bar name", "Yangtools", "Controller description ...",
+                contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry7 = createContributorListEntry(
+                "baz", "baz name", "Unknown Project",
+                "Unknown Project description ...", contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry8 = createContributorListEntry(
+                "pk", "pk name", "Unknown Project 2",
+                "Controller description ...", contributorListSchemaNode);
+
+        contributorMapBldr.addChild(contributorMapEntry1);
+        contributorMapBldr.addChild(contributorMapEntry2);
+        contributorMapBldr.addChild(contributorMapEntry3);
+        contributorMapBldr.addChild(contributorMapEntry4);
+        contributorMapBldr.addChild(contributorMapEntry5);
+        contributorMapBldr.addChild(contributorMapEntry6);
+        contributorMapBldr.addChild(contributorMapEntry7);
+        contributorMapBldr.addChild(contributorMapEntry8);
+
+        final MapNode contributorMap = contributorMapBldr.build();
+
+        return contributorMap;
+
+    }
+
+    private static MapEntryNode createContributorListEntry(
+            final String loginVal, final String contributorNameVal,
+            final String odlProjectNameVal, final String odlProjectDescVal,
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final LeafNode<String> loginLeaf = ImmutableNodes.leafNode(login,
+                loginVal);
+        final LeafNode<String> contributorNameLeaf = ImmutableNodes.leafNode(
+                contributorName, contributorNameVal);
+        final LeafNode<String> odlProjectNameLeafRef = ImmutableNodes.leafNode(
+                odlProjectName, odlProjectNameVal);
+        final LeafNode<String> odlProjectDescLeafRef = ImmutableNodes.leafNode(
+                odlProjectDesc, odlProjectDescVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> contributorMapEntryBldr = Builders
+                .mapEntryBuilder(contributorListSchemaNode);
+
+        contributorMapEntryBldr.addChild(loginLeaf);
+        contributorMapEntryBldr.addChild(contributorNameLeaf);
+        contributorMapEntryBldr.addChild(odlProjectNameLeafRef);
+        contributorMapEntryBldr.addChild(odlProjectDescLeafRef);
+
+        final MapEntryNode contributorMapEntry = contributorMapEntryBldr
+                .build();
+
+        return contributorMapEntry;
+    }
+
+    private static ContainerNode createOdlContainer(
+            final ContainerSchemaNode container) {
+
+        final ListSchemaNode projListSchemaNode = (ListSchemaNode) container
+                .getDataChildByName(project);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> odlProjectContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode projectMap = createProjectList(projListSchemaNode);
+        odlProjectContainerBldr.addChild(projectMap);
+
+        final ContainerNode odlProjectContainer = odlProjectContainerBldr
+                .build();
+
+        return odlProjectContainer;
+    }
+
+    private static MapNode createProjectList(
+            final ListSchemaNode projListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> projectMapBldr = Builders
+                .mapBuilder(projListSchemaNode);
+
+        final MapEntryNode projMapEntry1 = createProjectListEntry("Yangtools",
+                "Yangtools description ...", "Leader of Yangtools",
+                "Owner of Yangtools", projListSchemaNode);
+        final MapEntryNode projMapEntry2 = createProjectListEntry("MD-SAL",
+                "MD-SAL description ...", "Leader of MD-SAL",
+                "Owner of MD-SAL", projListSchemaNode);
+        final MapEntryNode projMapEntry3 = createProjectListEntry("Controller",
+                "Controller description ...", "Leader of Controller",
+                "Owner of Controller", projListSchemaNode);
+
+        projectMapBldr.addChild(projMapEntry1);
+        projectMapBldr.addChild(projMapEntry2);
+        projectMapBldr.addChild(projMapEntry3);
+
+        final MapNode projectMap = projectMapBldr.build();
+
+        return projectMap;
+    }
+
+    private static MapEntryNode createProjectListEntry(final String nameVal,
+            final String descVal, final String leadVal, final String ownerVal,
+            final ListSchemaNode projListSchemaNode) {
+
+        final LeafNode<String> nameLeaf = ImmutableNodes
+                .leafNode(name, nameVal);
+        final LeafNode<String> descLeaf = ImmutableNodes
+                .leafNode(desc, descVal);
+        final LeafNode<String> leadLeafRef = ImmutableNodes.leafNode(lead,
+                leadVal);
+        final LeafNode<String> ownerLeafRef = ImmutableNodes.leafNode(owner,
+                ownerVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> projMapEntryBldr = Builders
+                .mapEntryBuilder(projListSchemaNode);
+
+        projMapEntryBldr.addChild(nameLeaf);
+        projMapEntryBldr.addChild(descLeaf);
+        projMapEntryBldr.addChild(leadLeafRef);
+        projMapEntryBldr.addChild(ownerLeafRef);
+        final MapEntryNode projMapEntry = projMapEntryBldr.build();
+
+        return projMapEntry;
+    }
+
+    private static ContainerNode createBasicContributorContainer(
+            final ContainerSchemaNode contributorContSchemaNode) {
+
+        final ListSchemaNode contributorListSchemaNode = (ListSchemaNode) contributorContSchemaNode
+                .getDataChildByName(contributor);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> contributorContainerBldr = Builders
+                .containerBuilder(contributorContSchemaNode);
+
+        final MapNode contributorMap = createBasicContributorList(contributorListSchemaNode);
+        contributorContainerBldr.addChild(contributorMap);
+
+        final ContainerNode contributorContainer = contributorContainerBldr
+                .build();
+
+        return contributorContainer;
+
+    }
+
+    private static MapNode createBasicContributorList(
+            final ListSchemaNode contributorListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> contributorMapBldr = Builders
+                .mapBuilder(contributorListSchemaNode);
+
+        final MapEntryNode contributorMapEntry1 = createContributorListEntry(
+                "Leader of Yangtools", "Yangtools Leader name", "Yangtools",
+                "Yangtools description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry2 = createContributorListEntry(
+                "Leader of MD-SAL", "MD-SAL Leader name", "MD-SAL",
+                "MD-SAL description ...", contributorListSchemaNode);
+        final MapEntryNode contributorMapEntry3 = createContributorListEntry(
+                "Leader of Controller", "Controller Leader name", "Controller",
+                "Controller description ...", contributorListSchemaNode);
+
+        contributorMapBldr.addChild(contributorMapEntry1);
+        contributorMapBldr.addChild(contributorMapEntry2);
+        contributorMapBldr.addChild(contributorMapEntry3);
+
+        final MapNode contributorMap = contributorMapBldr.build();
+
+        return contributorMap;
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest2.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest2.java
new file mode 100644 (file)
index 0000000..3f26996
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidatation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest2 {
+
+    private static SchemaContext context;
+    private static Module mainModule;
+    private static QNameModule rootModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static TipProducingDataTree inMemoryDataTree;
+
+    private static QName chips;
+    private static QName chip;
+    private static QName devType;
+    private static QName chipDesc;
+
+    private static QName devices;
+    private static QName device;
+    private static QName typeChoice;
+    private static QName typeText;
+    private static QName devDesc;
+    private static QName sn;
+    private static QName defaultIp;
+
+    private static QName deviceTypeStr;
+    private static QName deviceType;
+    private static QName type;
+    private static QName desc;
+
+    private static final Logger LOG = LoggerFactory.getLogger("");
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws FileNotFoundException, ReactorException, URISyntaxException {
+
+        initSchemaContext();
+        initLeafRefContext();
+        initQnames();
+        initDataTree();
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest2() {
+
+        writeDevices();
+    }
+
+    private static void writeDevices() {
+
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.getDataChildByName(devices);
+
+        final ContainerNode devicesContainer = createDevicesContainer(devicesContSchemaNode);
+
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification writeModification = inMemoryDataTree.takeSnapshot().newModification();
+        writeModification.write(devicesPath, devicesContainer);
+
+        writeModification.ready();
+        final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree.prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+
+            assertEquals(4, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After write: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+    }
+
+    private static void initQnames() {
+
+        chips = QName.create(rootModuleQname, "chips");
+        chip = QName.create(rootModuleQname, "chip");
+        devType = QName.create(rootModuleQname, "dev_type");
+        chipDesc = QName.create(rootModuleQname, "chip_desc");
+
+        devices = QName.create(rootModuleQname, "devices");
+        device = QName.create(rootModuleQname, "device");
+        typeText = QName.create(rootModuleQname, "type_text");
+        devDesc = QName.create(rootModuleQname, "dev_desc");
+        sn = QName.create(rootModuleQname, "sn");
+        defaultIp = QName.create(rootModuleQname, "default_ip");
+
+        deviceTypeStr = QName.create(rootModuleQname, "device_types");
+        deviceType = QName.create(rootModuleQname, "device_type");
+        type = QName.create(rootModuleQname, "type");
+        desc = QName.create(rootModuleQname, "desc");
+    }
+
+    private static void initSchemaContext() throws URISyntaxException, FileNotFoundException, ReactorException {
+
+        final File resourceFile = new File(DataTreeCandidateValidatorTest.class.getResource(
+                "/leafref-validation/leafref-validation2.yang").toURI());
+        final File resourceDir = resourceFile.getParentFile();
+
+        context = RetestUtils.parseYangSources(Arrays.asList(resourceDir.listFiles()));
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation2")) {
+                mainModule = module;
+            }
+        }
+
+        rootModuleQname = mainModule.getQNameModule();
+    }
+
+    private static void initDataTree() {
+
+        inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(context);
+
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        final ContainerSchemaNode chipsListContSchemaNode = (ContainerSchemaNode) mainModule.getDataChildByName(chips);
+        final ContainerNode chipsContainer = createChipsContainer(chipsListContSchemaNode);
+        final YangInstanceIdentifier path1 = YangInstanceIdentifier.of(chips);
+        initialDataTreeModification.write(path1, chipsContainer);
+
+        final ContainerSchemaNode devTypesListContSchemaNode = (ContainerSchemaNode) mainModule
+                .getDataChildByName(deviceTypeStr);
+        final ContainerNode deviceTypesContainer = createDevTypeStrContainer(devTypesListContSchemaNode);
+        final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
+        initialDataTreeModification.write(path2, deviceTypesContainer);
+
+        initialDataTreeModification.ready();
+        final DataTreeCandidate writeChipsCandidate = inMemoryDataTree.prepare(initialDataTreeModification);
+
+        inMemoryDataTree.commit(writeChipsCandidate);
+
+        System.out.println(inMemoryDataTree.toString());
+    }
+
+    private static void initLeafRefContext() {
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    private static ContainerNode createDevTypeStrContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devTypeListSchemaNode = (ListSchemaNode) container.getDataChildByName(deviceType);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> devTypeContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devTypeMap = createDevTypeList(devTypeListSchemaNode);
+        devTypeContainerBldr.addChild(devTypeMap);
+
+        return devTypeContainerBldr.build();
+    }
+
+    private static MapNode createDevTypeList(final ListSchemaNode devTypeListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devTypeMapBldr = Builders.mapBuilder(devTypeListSchemaNode);
+
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_1", "typedesc1", devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_2", "typedesc2", devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type_3", "typedesc3", devTypeListSchemaNode));
+
+        return devTypeMapBldr.build();
+    }
+
+    private static MapEntryNode createDevTypeListEntry(final String typeVal, final String descVal,
+            final ListSchemaNode devTypeListSchemaNode) {
+
+        final LeafNode<String> typeLeaf = ImmutableNodes.leafNode(type, typeVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(desc, descVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> devTypeMapEntryBldr = Builders
+                .mapEntryBuilder(devTypeListSchemaNode);
+
+        devTypeMapEntryBldr.addChild(typeLeaf);
+        devTypeMapEntryBldr.addChild(descLeaf);
+
+        return devTypeMapEntryBldr.build();
+    }
+
+    private static ContainerNode createChipsContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode chipsListSchemaNode = (ListSchemaNode) container.getDataChildByName(chip);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> chipsContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode chipsMap = createChipsList(chipsListSchemaNode);
+        chipsContainerBldr.addChild(chipsMap);
+
+        return chipsContainerBldr.build();
+    }
+
+    private static MapNode createChipsList(final ListSchemaNode chipsListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> chipsMapBldr = Builders.mapBuilder(chipsListSchemaNode);
+
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_1", "desc1", chipsListSchemaNode));
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_2", "desc2", chipsListSchemaNode));
+
+        return chipsMapBldr.build();
+    }
+
+    private static MapEntryNode createChipsListEntry(final String devTypeVal, final String chipDescVal,
+            final ListSchemaNode chipsListSchemaNode) {
+
+        final LeafNode<String> devTypeLeaf = ImmutableNodes.leafNode(devType, devTypeVal);
+        final LeafNode<String> chipDescLeaf = ImmutableNodes.leafNode(chipDesc, chipDescVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> chipsMapEntryBldr = Builders
+                .mapEntryBuilder(chipsListSchemaNode);
+
+        chipsMapEntryBldr.addChild(devTypeLeaf);
+        chipsMapEntryBldr.addChild(chipDescLeaf);
+
+        return chipsMapEntryBldr.build();
+    }
+
+    private static ContainerNode createDevicesContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.getDataChildByName(device);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDeviceList(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDeviceList(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_1", "typedesc1", 123456, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_2", "typedesc2", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_2", "typedesc3", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type_1", "typedesc2", 123458, "192.168.0.1",
+                deviceListSchemaNode));
+        devicesMapBldr
+                .addChild(createDeviceListEntry("unknown", "unknown", 123457, "192.168.0.1", deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static MapEntryNode createDeviceListEntry(final String typeTextVal, final String descVal, final int snVal,
+            final String defaultIpVal, final ListSchemaNode devicesListSchemaNode) {
+
+        final LeafNode<String> typeTextLeaf = ImmutableNodes.leafNode(typeText, typeTextVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(devDesc, descVal);
+        final LeafNode<Integer> snValLeaf = ImmutableNodes.leafNode(sn, snVal);
+        final LeafNode<String> defaultIpLeaf = ImmutableNodes.leafNode(defaultIp, defaultIpVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> devicesMapEntryBldr = Builders
+                .mapEntryBuilder(devicesListSchemaNode);
+
+        devicesMapEntryBldr.addChild(typeTextLeaf);
+        devicesMapEntryBldr.addChild(descLeaf);
+        devicesMapEntryBldr.addChild(snValLeaf);
+        devicesMapEntryBldr.addChild(defaultIpLeaf);
+
+        return devicesMapEntryBldr.build();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest3.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/DataTreeCandidateValidatorTest3.java
new file mode 100644 (file)
index 0000000..4f7a2dd
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Set;
+import org.apache.log4j.BasicConfigurator;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TipProducingDataTree;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefDataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefValidatation;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeAttrBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.tree.InMemoryDataTreeFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DataTreeCandidateValidatorTest3 {
+
+    private static SchemaContext context;
+    private static Module mainModule;
+    private static QNameModule rootModuleQname;
+    private static LeafRefContext rootLeafRefContext;
+    public static TipProducingDataTree inMemoryDataTree;
+
+    private static QName chips;
+    private static QName chip;
+    private static QName devType;
+    private static QName chipDesc;
+
+    private static QName devices;
+    private static QName device;
+    private static QName typeText1;
+    private static QName typeText2;
+    private static QName typeText3;
+    private static QName devDesc;
+    private static QName sn;
+    private static QName defaultIp;
+
+    private static QName deviceTypeStr;
+    private static QName deviceType;
+    private static QName type1;
+    private static QName type2;
+    private static QName type3;
+    private static QName desc;
+
+    private static final Logger LOG = LoggerFactory.getLogger("");
+    private static final String NEW_LINE = System.getProperty("line.separator");
+
+    static {
+        BasicConfigurator.configure();
+    }
+
+    @BeforeClass
+    public static void init() throws FileNotFoundException, ReactorException, URISyntaxException {
+
+        initSchemaContext();
+        initLeafRefContext();
+        initQnames();
+        initDataTree();
+    }
+
+    @Test
+    public void dataTreeCanditateValidationTest2() {
+
+        writeDevices();
+
+        mergeDevices();
+    }
+
+    private static void writeDevices() {
+
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.getDataChildByName(devices);
+
+        final ContainerNode devicesContainer = createDevicesContainer(devicesContSchemaNode);
+
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification writeModification = inMemoryDataTree.takeSnapshot().newModification();
+        writeModification.write(devicesPath, devicesContainer);
+
+        writeModification.ready();
+        final DataTreeCandidate writeDevicesCandidate = inMemoryDataTree.prepare(writeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(writeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        assertTrue(exception);
+
+        inMemoryDataTree.commit(writeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After writeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+    }
+
+    private static void mergeDevices() {
+
+        final ContainerSchemaNode devicesContSchemaNode = (ContainerSchemaNode) mainModule.getDataChildByName(devices);
+
+        final ContainerNode devicesContainer = createDevices2Container(devicesContSchemaNode);
+
+        final YangInstanceIdentifier devicesPath = YangInstanceIdentifier.of(devices);
+        final DataTreeModification mergeModification = inMemoryDataTree.takeSnapshot().newModification();
+        mergeModification.write(devicesPath, devicesContainer);
+        mergeModification.merge(devicesPath, devicesContainer);
+
+        mergeModification.ready();
+        final DataTreeCandidate mergeDevicesCandidate = inMemoryDataTree.prepare(mergeModification);
+
+        LOG.debug("*************************");
+        LOG.debug("Before mergeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        boolean exception = false;
+        try {
+            LeafRefValidatation.validate(mergeDevicesCandidate, rootLeafRefContext);
+        } catch (final LeafRefDataValidationFailedException e) {
+            LOG.debug("All validation errors:" + NEW_LINE + e.getMessage());
+            // :TODO verify errors count gz
+            assertEquals(6, e.getValidationsErrorsCount());
+            exception = true;
+        }
+
+        inMemoryDataTree.commit(mergeDevicesCandidate);
+
+        LOG.debug("*************************");
+        LOG.debug("After mergeDevices: ");
+        LOG.debug("*************************");
+        LOG.debug(inMemoryDataTree.toString());
+
+        assertTrue(exception);
+    }
+
+    private static void initQnames() {
+
+        chips = QName.create(rootModuleQname, "chips");
+        chip = QName.create(rootModuleQname, "chip");
+        devType = QName.create(rootModuleQname, "dev_type");
+        chipDesc = QName.create(rootModuleQname, "chip_desc");
+
+        devices = QName.create(rootModuleQname, "devices");
+        device = QName.create(rootModuleQname, "device");
+        typeText1 = QName.create(rootModuleQname, "type_text1");
+        typeText2 = QName.create(rootModuleQname, "type_text2");
+        typeText3 = QName.create(rootModuleQname, "type_text3");
+        devDesc = QName.create(rootModuleQname, "dev_desc");
+        sn = QName.create(rootModuleQname, "sn");
+        defaultIp = QName.create(rootModuleQname, "default_ip");
+
+        deviceTypeStr = QName.create(rootModuleQname, "device_types");
+        deviceType = QName.create(rootModuleQname, "device_type");
+        type1 = QName.create(rootModuleQname, "type1");
+        type2 = QName.create(rootModuleQname, "type2");
+        type3 = QName.create(rootModuleQname, "type3");
+        desc = QName.create(rootModuleQname, "desc");
+    }
+
+    private static void initSchemaContext() throws URISyntaxException, FileNotFoundException, ReactorException {
+
+        final File resourceFile = new File(DataTreeCandidateValidatorTest.class.getResource(
+                "/leafref-validation/leafref-validation3.yang").toURI());
+        final File resourceDir = resourceFile.getParentFile();
+
+        context = RetestUtils.parseYangSources(Arrays.asList(resourceDir.listFiles()));
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-validation3")) {
+                mainModule = module;
+            }
+        }
+
+        rootModuleQname = mainModule.getQNameModule();
+    }
+
+    private static void initDataTree() {
+
+        inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(context);
+
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+
+        final ContainerSchemaNode chipsListContSchemaNode = (ContainerSchemaNode) mainModule.getDataChildByName(chips);
+        final ContainerNode chipsContainer = createChipsContainer(chipsListContSchemaNode);
+        final YangInstanceIdentifier path1 = YangInstanceIdentifier.of(chips);
+        initialDataTreeModification.write(path1, chipsContainer);
+
+        final ContainerSchemaNode devTypesListContSchemaNode = (ContainerSchemaNode) mainModule
+                .getDataChildByName(deviceTypeStr);
+        final ContainerNode deviceTypesContainer = createDevTypeStrContainer(devTypesListContSchemaNode);
+        final YangInstanceIdentifier path2 = YangInstanceIdentifier.of(deviceTypeStr);
+        initialDataTreeModification.write(path2, deviceTypesContainer);
+
+        initialDataTreeModification.ready();
+        final DataTreeCandidate writeChipsCandidate = inMemoryDataTree.prepare(initialDataTreeModification);
+
+        inMemoryDataTree.commit(writeChipsCandidate);
+
+        System.out.println(inMemoryDataTree.toString());
+    }
+
+    private static void initLeafRefContext() {
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    private static ContainerNode createDevTypeStrContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devTypeListSchemaNode = (ListSchemaNode) container.getDataChildByName(deviceType);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> devTypeContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devTypeMap = createDevTypeList(devTypeListSchemaNode);
+        devTypeContainerBldr.addChild(devTypeMap);
+
+        return devTypeContainerBldr.build();
+    }
+
+    private static MapNode createDevTypeList(final ListSchemaNode devTypeListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devTypeMapBldr = Builders.mapBuilder(devTypeListSchemaNode);
+
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_1", "dev_type2_1", "dev_type3_1", "typedesc1",
+                devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_2", "dev_type2_2", "dev_type3_2", "typedesc2",
+                devTypeListSchemaNode));
+        devTypeMapBldr.addChild(createDevTypeListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc3",
+                devTypeListSchemaNode));
+
+        return devTypeMapBldr.build();
+    }
+
+    private static MapEntryNode createDevTypeListEntry(final String type1Val, final String type2Val,
+            final String type3Val, final String descVal, final ListSchemaNode devTypeListSchemaNode) {
+
+        final LeafNode<String> type1Leaf = ImmutableNodes.leafNode(type1, type1Val);
+        final LeafNode<String> type2Leaf = ImmutableNodes.leafNode(type2, type2Val);
+        final LeafNode<String> type3Leaf = ImmutableNodes.leafNode(type3, type3Val);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(desc, descVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> devTypeMapEntryBldr = Builders
+                .mapEntryBuilder(devTypeListSchemaNode);
+
+        devTypeMapEntryBldr.addChild(type1Leaf);
+        devTypeMapEntryBldr.addChild(type2Leaf);
+        devTypeMapEntryBldr.addChild(type3Leaf);
+        devTypeMapEntryBldr.addChild(descLeaf);
+
+        return devTypeMapEntryBldr.build();
+    }
+
+    private static ContainerNode createChipsContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode chipsListSchemaNode = (ListSchemaNode) container.getDataChildByName(chip);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> chipsContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode chipsMap = createChipsList(chipsListSchemaNode);
+        chipsContainerBldr.addChild(chipsMap);
+
+        return chipsContainerBldr.build();
+    }
+
+    private static MapNode createChipsList(final ListSchemaNode chipsListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> chipsMapBldr = Builders.mapBuilder(chipsListSchemaNode);
+
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_1", "desc1", chipsListSchemaNode));
+        chipsMapBldr.addChild(createChipsListEntry("dev_type_2", "desc2", chipsListSchemaNode));
+
+        return chipsMapBldr.build();
+    }
+
+    private static MapEntryNode createChipsListEntry(final String devTypeVal, final String chipDescVal,
+            final ListSchemaNode chipsListSchemaNode) {
+
+        final LeafNode<String> devTypeLeaf = ImmutableNodes.leafNode(devType, devTypeVal);
+        final LeafNode<String> chipDescLeaf = ImmutableNodes.leafNode(chipDesc, chipDescVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> chipsMapEntryBldr = Builders
+                .mapEntryBuilder(chipsListSchemaNode);
+
+        chipsMapEntryBldr.addChild(devTypeLeaf);
+        chipsMapEntryBldr.addChild(chipDescLeaf);
+
+        return chipsMapEntryBldr.build();
+    }
+
+    private static ContainerNode createDevicesContainer(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.getDataChildByName(device);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDeviceList(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDeviceList(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_1", "dev_type2_1", "dev_type3_1", "typedesc1", 123456,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_2", "dev_type2_2", "dev_type3_2", "typedesc1", 123457,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_1", "dev_type2_2", "dev_type3_3", "typedesc2", 123458,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("unk11", "unk22", "unk33", "unk_desc2", 123457, "192.168.0.1",
+                deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static ContainerNode createDevices2Container(final ContainerSchemaNode container) {
+
+        final ListSchemaNode devicesListSchemaNode = (ListSchemaNode) container.getDataChildByName(device);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifier, ContainerNode> devicesContainerBldr = Builders
+                .containerBuilder(container);
+
+        final MapNode devicesMap = createDevice2List(devicesListSchemaNode);
+        devicesContainerBldr.addChild(devicesMap);
+
+        return devicesContainerBldr.build();
+    }
+
+    private static MapNode createDevice2List(final ListSchemaNode deviceListSchemaNode) {
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> devicesMapBldr = Builders.mapBuilder(deviceListSchemaNode);
+
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc3", 123459,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_3", "dev_type3_3", "typedesc2", 123460,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("dev_type1_3", "dev_type2_2", "dev_type3_1", "typedesc1", 123461,
+                "192.168.0.1", deviceListSchemaNode));
+        devicesMapBldr.addChild(createDeviceListEntry("unk1", "unk2", "unk3", "unk_desc", 123462, "192.168.0.1",
+                deviceListSchemaNode));
+
+        return devicesMapBldr.build();
+    }
+
+    private static MapEntryNode createDeviceListEntry(final String type1TextVal, final String type2TextVal,
+            final String type3TextVal, final String descVal, final int snVal, final String defaultIpVal,
+            final ListSchemaNode devicesListSchemaNode) {
+
+        final LeafNode<String> typeText1Leaf = ImmutableNodes.leafNode(typeText1, type1TextVal);
+        final LeafNode<String> typeText2Leaf = ImmutableNodes.leafNode(typeText2, type2TextVal);
+        final LeafNode<String> typeText3Leaf = ImmutableNodes.leafNode(typeText3, type3TextVal);
+        final LeafNode<String> descLeaf = ImmutableNodes.leafNode(devDesc, descVal);
+        final LeafNode<Integer> snValLeaf = ImmutableNodes.leafNode(sn, snVal);
+        final LeafNode<String> defaultIpLeaf = ImmutableNodes.leafNode(defaultIp, defaultIpVal);
+
+        final DataContainerNodeAttrBuilder<NodeIdentifierWithPredicates, MapEntryNode> devicesMapEntryBldr = Builders
+                .mapEntryBuilder(devicesListSchemaNode);
+
+        devicesMapEntryBldr.addChild(typeText1Leaf);
+        devicesMapEntryBldr.addChild(typeText2Leaf);
+        devicesMapEntryBldr.addChild(typeText3Leaf);
+        devicesMapEntryBldr.addChild(descLeaf);
+        devicesMapEntryBldr.addChild(snValLeaf);
+        devicesMapEntryBldr.addChild(defaultIpLeaf);
+
+        return devicesMapEntryBldr.build();
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTest.java
new file mode 100644 (file)
index 0000000..c4463ef
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContextUtils;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class LeafRefContextTest {
+
+    private static SchemaContext context;
+    private static Module rootMod;
+    private static QNameModule root;
+    private static LeafRefContext rootLeafRefContext;
+
+    @BeforeClass
+    public static void init() throws URISyntaxException, FileNotFoundException, ReactorException {
+
+        final File resourceFile = new File(LeafRefContextTreeBuilderTest.class.getResource(
+                "/leafref-context-test/correct-modules/leafref-test2.yang").toURI());
+
+        final File resourceDir = resourceFile.getParentFile();
+
+        context = RetestUtils.parseYangSources(Arrays.asList(resourceDir.listFiles()));
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("leafref-test2")) {
+                rootMod = module;
+            }
+        }
+
+        root = rootMod.getQNameModule();
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    @Test
+    public void test() {
+
+        final QName q1 = QName.create(root, "ref1");
+        final QName q2 = QName.create(root, "leaf1");
+        final QName q3 = QName.create(root, "cont1");
+        final QName q4 = QName.create(root, "cont2");
+        final QName q5 = QName.create(root, "list1");
+        final QName q6 = QName.create(root, "name");
+
+        final DataSchemaNode leafRefNode = rootMod.getDataChildByName(q1);
+        final DataSchemaNode targetNode = rootMod.getDataChildByName(q2);
+        final DataSchemaNode cont1Node = rootMod.getDataChildByName(q3);
+        final DataSchemaNode cont2Node = rootMod.getDataChildByName(q4);
+        final DataSchemaNode name1Node = ((DataNodeContainer) ((DataNodeContainer) rootMod.getDataChildByName(q3))
+                .getDataChildByName(q5)).getDataChildByName(q6);
+
+        assertTrue(LeafRefContextUtils.isLeafRef(leafRefNode, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.isLeafRef(targetNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.hasLeafRefChild(cont1Node, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.hasLeafRefChild(leafRefNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.isReferencedByLeafRef(targetNode, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.isReferencedByLeafRef(leafRefNode, rootLeafRefContext));
+
+        assertTrue(LeafRefContextUtils.hasChildReferencedByLeafRef(cont2Node, rootLeafRefContext));
+        assertFalse(LeafRefContextUtils.hasChildReferencedByLeafRef(leafRefNode, rootLeafRefContext));
+
+        Map<QName, LeafRefContext> leafRefs = LeafRefContextUtils.getAllLeafRefsReferencingThisNode(name1Node,
+                rootLeafRefContext);
+        assertEquals(4, leafRefs.size());
+        leafRefs = LeafRefContextUtils.getAllLeafRefsReferencingThisNode(leafRefNode, rootLeafRefContext);
+        assertTrue(leafRefs.isEmpty());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTreeBuilderTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/leafref/context/test/retest/LeafRefContextTreeBuilderTest.java
new file mode 100644 (file)
index 0000000..cd4140b
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContext;
+import org.opendaylight.yangtools.yang.data.impl.leafref.LeafRefContextUtils;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class LeafRefContextTreeBuilderTest {
+
+    private static SchemaContext context;
+    private static Module impMod;
+    private static Module tstMod;
+    private static QNameModule imp;
+    private static QNameModule tst;
+    private static LeafRefContext rootLeafRefContext;
+
+    @BeforeClass
+    public static void init() throws URISyntaxException, FileNotFoundException, ReactorException {
+        final File resourceFile = new File(LeafRefContextTreeBuilderTest.class.getResource(
+                "/leafref-context-test/correct-modules/leafref-test.yang").toURI());
+        final File resourceDir = resourceFile.getParentFile();
+
+        context = RetestUtils.parseYangSources(Arrays.asList(resourceDir.listFiles()));
+
+        final Set<Module> modules = context.getModules();
+        for (final Module module : modules) {
+            if (module.getName().equals("import-mod")) {
+                impMod = module;
+            }
+            if (module.getName().equals("leafref-test")) {
+                tstMod = module;
+            }
+        }
+
+        imp = impMod.getQNameModule();
+        tst = tstMod.getQNameModule();
+
+        rootLeafRefContext = LeafRefContext.create(context);
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest1() {
+
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q3 = QName.create(tst, "project-lead");
+
+        final LeafRefContext leafRefCtx = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q3);
+
+        assertTrue(leafRefCtx.isReferencing());
+        assertNotNull(leafRefCtx.getLeafRefTargetPath());
+        assertFalse(leafRefCtx.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        System.out.println();
+        System.out.println("******* Test 1 ************");
+        System.out.println("Original definition string:");
+        System.out.println(leafRefCtx.getLeafRefTargetPathString());
+        System.out.println("Parsed leafref path:");
+        System.out.println(leafRefCtx.getLeafRefTargetPath().toString());
+        System.out.println("Absolute leafref path:");
+        System.out.println(leafRefCtx.getAbsoluteLeafRefTargetPath().toString());
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest2() {
+
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q4 = QName.create(tst, "project-lead2");
+
+        final LeafRefContext leafRefCtx2 = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q4);
+
+        assertTrue(leafRefCtx2.isReferencing());
+        assertNotNull(leafRefCtx2.getLeafRefTargetPath());
+        assertTrue(leafRefCtx2.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx2.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx2.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        System.out.println();
+        System.out.println("******* Test 2 ************");
+        System.out.println("Original definition string2:");
+        System.out.println(leafRefCtx2.getLeafRefTargetPathString());
+        System.out.println("Parsed leafref path2:");
+        System.out.println(leafRefCtx2.getLeafRefTargetPath().toString());
+        System.out.println("Absolute leafref path2:");
+        System.out.println(leafRefCtx2.getAbsoluteLeafRefTargetPath().toString());
+        System.out.println();
+
+    }
+
+    @Test
+    public void buildLeafRefContextTreeXPathTest() {
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q5 = QName.create(tst, "ch1");
+        final QName q6 = QName.create(tst, "c1");
+        final QName q7 = QName.create(tst, "ch2");
+        final QName q8 = QName.create(tst, "l1");
+        final LeafRefContext leafRefCtx3 = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q5).getReferencingChildByName(q6)
+                .getReferencingChildByName(q7).getReferencingChildByName(q6).getReferencingChildByName(q8);
+
+        assertTrue(leafRefCtx3.isReferencing());
+        assertNotNull(leafRefCtx3.getLeafRefTargetPath());
+        assertFalse(leafRefCtx3.getLeafRefTargetPath().isAbsolute());
+        assertNotNull(leafRefCtx3.getAbsoluteLeafRefTargetPath());
+        assertTrue(leafRefCtx3.getAbsoluteLeafRefTargetPath().isAbsolute());
+
+        System.out.println();
+        System.out.println("******* Test 3 ************");
+        System.out.println("Original definition string2:");
+        System.out.println(leafRefCtx3.getLeafRefTargetPathString());
+        System.out.println("Parsed leafref path2:");
+        System.out.println(leafRefCtx3.getLeafRefTargetPath().toString());
+        System.out.println("Absolute leafref path2:");
+        System.out.println(leafRefCtx3.getAbsoluteLeafRefTargetPath().toString());
+        System.out.println();
+    }
+
+    @Test
+    public void buildLeafRefContextTreeTest4() {
+        final QName q9 = QName.create(tst, "odl-project");
+        final QName q10 = QName.create(tst, "project");
+        final QName q11 = QName.create(tst, "name");
+
+        final LeafRefContext leafRefCtx4 = rootLeafRefContext.getReferencedChildByName(q9)
+                .getReferencedChildByName(q10).getReferencedChildByName(q11);
+
+        assertNotNull(leafRefCtx4);
+        assertTrue(leafRefCtx4.isReferenced());
+        assertEquals(6, leafRefCtx4.getAllReferencedByLeafRefCtxs().size());
+
+    }
+
+    @Test
+    public void leafRefContextUtilsTest() {
+        final QName q1 = QName.create(tst, "odl-contributor");
+        final QName q2 = QName.create(tst, "contributor");
+        final QName q3 = QName.create(tst, "odl-project-name");
+
+        final LeafRefContext odlContrProjNameCtx = rootLeafRefContext.getReferencingChildByName(q1)
+                .getReferencingChildByName(q2).getReferencingChildByName(q3);
+
+        final DataSchemaNode odlContrProjNameNode = ((DataNodeContainer) ((DataNodeContainer) tstMod
+                .getDataChildByName(q1)).getDataChildByName(q2)).getDataChildByName(q3);
+
+        final LeafRefContext foundOdlContrProjNameCtx = LeafRefContextUtils.getLeafRefReferencingContext(
+                odlContrProjNameNode, rootLeafRefContext);
+
+        assertNotNull(foundOdlContrProjNameCtx);
+        assertTrue(foundOdlContrProjNameCtx.isReferencing());
+        assertNotNull(foundOdlContrProjNameCtx.getLeafRefTargetPath());
+        assertEquals(odlContrProjNameCtx, foundOdlContrProjNameCtx);
+    }
+
+    @Test
+    public void leafRefContextUtilsTest2() {
+        final QName q1 = QName.create(tst, "odl-project");
+        final QName q2 = QName.create(tst, "project");
+        final QName q3 = QName.create(tst, "name");
+
+        final LeafRefContext leafRefCtx = rootLeafRefContext.getReferencedChildByName(q1).getReferencedChildByName(q2)
+                .getReferencedChildByName(q3);
+
+        final DataSchemaNode odlProjNameNode = ((DataNodeContainer) ((DataNodeContainer) tstMod.getDataChildByName(q1))
+                .getDataChildByName(q2)).getDataChildByName(q3);
+
+        LeafRefContext foundOdlProjNameCtx = LeafRefContextUtils.getLeafRefReferencingContext(odlProjNameNode,
+                rootLeafRefContext);
+
+        assertNull(foundOdlProjNameCtx);
+
+        foundOdlProjNameCtx = LeafRefContextUtils.getLeafRefReferencedByContext(odlProjNameNode, rootLeafRefContext);
+
+        assertNotNull(foundOdlProjNameCtx);
+        assertTrue(foundOdlProjNameCtx.isReferenced());
+        assertFalse(foundOdlProjNameCtx.getAllReferencedByLeafRefCtxs().isEmpty());
+        assertEquals(6, foundOdlProjNameCtx.getAllReferencedByLeafRefCtxs().size());
+        assertEquals(leafRefCtx, foundOdlProjNameCtx);
+    }
+
+    @Test
+    public void leafRefContextUtilsTest3() {
+        final QName q16 = QName.create(tst, "con1");
+        final DataSchemaNode con1 = tstMod.getDataChildByName(q16);
+        final List<LeafRefContext> allLeafRefChilds = LeafRefContextUtils
+                .findAllLeafRefChilds(con1, rootLeafRefContext);
+
+        assertNotNull(allLeafRefChilds);
+        assertFalse(allLeafRefChilds.isEmpty());
+        assertEquals(4, allLeafRefChilds.size());
+
+        final QName q17 = QName.create(tst, "odl-contributor");
+        final DataSchemaNode odlContributorNode = tstMod.getDataChildByName(q17);
+        List<LeafRefContext> allChildsReferencedByLeafRef = LeafRefContextUtils.findAllChildsReferencedByLeafRef(
+                odlContributorNode, rootLeafRefContext);
+
+        assertNotNull(allChildsReferencedByLeafRef);
+        assertFalse(allChildsReferencedByLeafRef.isEmpty());
+        assertEquals(1, allChildsReferencedByLeafRef.size());
+
+        allChildsReferencedByLeafRef = LeafRefContextUtils.findAllChildsReferencedByLeafRef(con1, rootLeafRefContext);
+
+        assertNotNull(allChildsReferencedByLeafRef);
+        assertTrue(allChildsReferencedByLeafRef.isEmpty());
+
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void incorrectLeafRefPathTest() throws URISyntaxException, FileNotFoundException, ReactorException {
+        final File resourceFile = new File(getClass().getResource(
+                "/leafref-context-test/incorrect-modules/leafref-test.yang").toURI());
+        final File resourceDir = resourceFile.getParentFile();
+
+        final SchemaContext context = RetestUtils.parseYangSources(Arrays.asList(resourceDir.listFiles()));
+
+        LeafRefContext.create(context);
+
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/InstanceIdToNodesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/InstanceIdToNodesTest.java
new file mode 100644 (file)
index 0000000..4a879b3
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.retest;
+
+import static org.junit.Assert.assertEquals;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class InstanceIdToNodesTest {
+
+    private static final String NS = "urn:opendaylight:params:xml:ns:yang:controller:md:sal:normalization:test";
+    private static final String REVISION = "2014-03-13";
+    private static final QName ID = QName.create(NS, REVISION, "id");
+    private static SchemaContext ctx;
+
+    private final YangInstanceIdentifier.NodeIdentifier rootContainer = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "test"));
+    private final YangInstanceIdentifier.NodeIdentifier outerContainer = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "outer-container"));
+    private final YangInstanceIdentifier.NodeIdentifier augmentedLeaf = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "augmented-leaf"));
+    private final YangInstanceIdentifier.AugmentationIdentifier augmentation = new YangInstanceIdentifier.AugmentationIdentifier(
+            Collections.singleton(augmentedLeaf.getNodeType()));
+
+    private final YangInstanceIdentifier.NodeIdentifier outerList = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "outer-list"));
+    private final YangInstanceIdentifier.NodeIdentifierWithPredicates outerListWithKey = new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+            QName.create(NS, REVISION, "outer-list"), ID, 1);
+    private final YangInstanceIdentifier.NodeIdentifier choice = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "outer-choice"));
+    private final YangInstanceIdentifier.NodeIdentifier leafFromCase = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "one"));
+
+    private final YangInstanceIdentifier.NodeIdentifier leafList = new YangInstanceIdentifier.NodeIdentifier(
+            QName.create(NS, REVISION, "ordered-leaf-list"));
+    private final YangInstanceIdentifier.NodeWithValue leafListWithValue = new YangInstanceIdentifier.NodeWithValue(
+            leafList.getNodeType(), "abcd");
+
+    static SchemaContext createTestContext() throws URISyntaxException, FileNotFoundException, ReactorException {
+        final File resourceFile = new File(InstanceIdToNodesTest.class.getResource("/filter-test.yang").toURI());
+        return RetestUtils.parseYangSources(resourceFile);
+    }
+
+    @BeforeClass
+    public static void setUp() throws Exception {
+        ctx = createTestContext();
+
+    }
+
+    @Test
+    public void testInAugment() throws Exception {
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(outerContainer)
+                                .withChild(
+                                        Builders.augmentationBuilder()
+                                                .withNodeIdentifier(augmentation)
+                                                .withChild(
+                                                        Builders.leafBuilder().withNodeIdentifier(augmentedLeaf)
+                                                                .build()).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testInAugmentLeafOverride() throws Exception {
+        final LeafNode<Object> lastLeaf = Builders.leafBuilder().withNodeIdentifier(augmentedLeaf)
+                .withValue("randomValue").build();
+
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.containerBuilder()
+                                .withNodeIdentifier(outerContainer)
+                                .withChild(
+                                        Builders.augmentationBuilder().withNodeIdentifier(augmentation)
+                                                .withChild(lastLeaf).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerContainer, augmentation, augmentedLeaf), lastLeaf);
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testListChoice() throws Exception {
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.mapBuilder()
+                                .withNodeIdentifier(outerList)
+                                .withChild(
+                                        Builders.mapEntryBuilder()
+                                                .withNodeIdentifier(outerListWithKey)
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        new YangInstanceIdentifier.NodeIdentifier(ID))
+                                                                .withValue(1).build())
+                                                .withChild(
+                                                        Builders.choiceBuilder()
+                                                                .withNodeIdentifier(choice)
+                                                                .withChild(
+                                                                        Builders.leafBuilder()
+                                                                                .withNodeIdentifier(leafFromCase)
+                                                                                .build()).build()).build()).build())
+                .build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey, choice, leafFromCase));
+        assertEquals(expectedFilter, filter);
+    }
+
+    @Test
+    public void testTopContainerLastChildOverride() throws Exception {
+        final ContainerNode expectedStructure = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.mapBuilder()
+                                .withNodeIdentifier(outerList)
+                                .withChild(
+                                        Builders.mapEntryBuilder()
+                                                .withNodeIdentifier(outerListWithKey)
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        new YangInstanceIdentifier.NodeIdentifier(ID))
+                                                                .withValue(1).build())
+                                                .withChild(
+                                                        Builders.choiceBuilder()
+                                                                .withNodeIdentifier(choice)
+                                                                .withChild(
+                                                                        Builders.leafBuilder()
+                                                                                .withNodeIdentifier(leafFromCase)
+                                                                                .build()).build()).build()).build())
+                .build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer), expectedStructure);
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testListLastChildOverride() throws Exception {
+        final MapEntryNode outerListEntry = Builders
+                .mapEntryBuilder()
+                .withNodeIdentifier(outerListWithKey)
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(ID))
+                                .withValue(1).build()).build();
+        final MapNode lastChild = Builders.mapBuilder().withNodeIdentifier(this.outerList).withChild(outerListEntry)
+                .build();
+        final ContainerNode expectedStructure = Builders.containerBuilder().withNodeIdentifier(rootContainer)
+                .withChild(lastChild).build();
+
+        NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey), outerListEntry);
+        assertEquals(expectedStructure, filter);
+        filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, outerList, outerListWithKey));
+        assertEquals(expectedStructure, filter);
+    }
+
+    @Test
+    public void testLeafList() throws Exception {
+        final ContainerNode expectedFilter = Builders
+                .containerBuilder()
+                .withNodeIdentifier(rootContainer)
+                .withChild(
+                        Builders.orderedLeafSetBuilder()
+                                .withNodeIdentifier(leafList)
+                                .withChild(
+                                        Builders.leafSetEntryBuilder().withNodeIdentifier(leafListWithValue)
+                                                .withValue(leafListWithValue.getValue()).build()).build()).build();
+
+        final NormalizedNode<?, ?> filter = ImmutableNodes.fromInstanceId(ctx,
+                YangInstanceIdentifier.create(rootContainer, leafList, leafListWithValue));
+        assertEquals(expectedFilter, filter);
+    }
+}
\ No newline at end of file
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/NormalizedDataBuilderTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/retest/NormalizedDataBuilderTest.java
new file mode 100644 (file)
index 0000000..a01c596
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.retest;
+
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.leafref.context.test.retest.LeafRefContextTreeBuilderTest;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableChoiceNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableMapNodeSchemaAwareBuilder;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class NormalizedDataBuilderTest {
+
+    private ContainerSchemaNode containerNode;
+    private SchemaContext schema;
+
+    @Before
+    public void setUp() throws URISyntaxException, FileNotFoundException, ReactorException {
+        final File resourceFile = new File(getClass().getResource(
+                "../test.yang").toURI());
+        schema = RetestUtils.parseYangSources(resourceFile);
+        containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+    }
+
+    @Test
+    public void testSchemaUnaware() {
+        // Container
+        DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder = Builders
+                .containerBuilder().withNodeIdentifier(getNodeIdentifier("container"));
+
+        // leaf
+        LeafNode<String> leafChild = Builders.<String> leafBuilder().withNodeIdentifier(getNodeIdentifier("leaf"))
+                .withValue("String").build();
+        builder.withChild(leafChild);
+
+        // leafList
+        LeafSetNode<Integer> leafList = Builders
+                .<Integer> leafSetBuilder()
+                .withNodeIdentifier(getNodeIdentifier("leaf"))
+                .withChildValue(1)
+                .withChild(
+                        Builders.<Integer> leafSetEntryBuilder()
+                                .withNodeIdentifier(getNodeWithValueIdentifier("leaf", 3)).withValue(3).build())
+                .build();
+        builder.withChild(leafList);
+
+        // list
+        MapEntryNode listChild1 = Builders
+                .mapEntryBuilder()
+                .withChild(
+                        Builders.<Integer> leafBuilder().withNodeIdentifier(getNodeIdentifier("uint32InList"))
+                                .withValue(1).build())
+                .withChild(Builders.containerBuilder().withNodeIdentifier(getNodeIdentifier("containerInList")).build())
+                .withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                                getNodeIdentifier("list").getNodeType(), Collections.singletonMap(
+                                getNodeIdentifier("uint32InList").getNodeType(), (Object) 1))).build();
+
+        MapNode list = Builders.mapBuilder().withChild(listChild1).withNodeIdentifier(getNodeIdentifier("list"))
+                .build();
+        builder.withChild(list);
+
+        AugmentationNode augmentation = Builders
+                .augmentationBuilder()
+                .withNodeIdentifier(
+                        new YangInstanceIdentifier.AugmentationIdentifier(Sets.newHashSet(getQName("augmentUint32"))))
+                .withChild(
+                        Builders.<Integer>leafBuilder().withNodeIdentifier(getNodeIdentifier("augmentUint32"))
+                                .withValue(11).build()).build();
+
+        builder.withChild(augmentation);
+
+        // This works without schema (adding child from augment as a direct
+        // child)
+        builder.withChild(Builders.<Integer> leafBuilder().withNodeIdentifier(getNodeIdentifier("augmentUint32"))
+                .withValue(11).build());
+    }
+
+    @Test
+    public void testSchemaAware() {
+        DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> builder = Builders
+                .containerBuilder(containerNode);
+
+        LeafSchemaNode schemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "uint32");
+        LeafNode<String> leafChild = Builders.<String> leafBuilder(schemaNode).withValue("String").build();
+        builder.withChild(leafChild);
+
+        LeafListSchemaNode leafListSchemaNode = (LeafListSchemaNode) getSchemaNode(schema, "test", "leafList");
+        LeafSetNode<Integer> leafList = Builders.<Integer> leafSetBuilder(leafListSchemaNode).withChildValue(1)
+                .withChild(Builders.<Integer> leafSetEntryBuilder(leafListSchemaNode).withValue(3).build()).build();
+        builder.withChild(leafList);
+
+        ListSchemaNode listSchema = (ListSchemaNode) getSchemaNode(schema, "test", "list");
+        LeafSchemaNode uint32InListSchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "uint32InList");
+        ContainerSchemaNode containerInListSchemaNode = (ContainerSchemaNode) getSchemaNode(schema, "test",
+                "containerInList");
+
+        MapEntryNode listChild1 = Builders.mapEntryBuilder(listSchema)
+                .withChild(Builders.<Integer> leafBuilder(uint32InListSchemaNode).withValue(1).build())
+                .withChild(Builders.containerBuilder(containerInListSchemaNode).build()).build();
+
+        MapNode list = ImmutableMapNodeSchemaAwareBuilder.create(listSchema).withChild(listChild1).build();
+        builder.withChild(list);
+
+        LeafSchemaNode augmentUint32SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentUint32");
+        AugmentationSchema augmentationSchema = getAugmentationSchemaForChild(containerNode,
+                augmentUint32SchemaNode.getQName());
+
+        AugmentationNode augmentation = Builders.augmentationBuilder(augmentationSchema)
+                .withChild(Builders.<Integer> leafBuilder(augmentUint32SchemaNode).withValue(11).build()).build();
+
+        builder.withChild(augmentation);
+
+        // This should fail with schema, since the leaf comes from augmentation
+        // builder.withChild(ImmutableLeafNodeSchemaAwareBuilder.<Integer>get(augmentUint32SchemaNode).withValue(11).build());
+
+        LeafSchemaNode augumentString1SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentString1");
+        LeafSchemaNode augumentString2SchemaNode = (LeafSchemaNode) getSchemaNode(schema, "test", "augmentString2");
+
+        ChoiceSchemaNode choice1SchemaNode = (ChoiceSchemaNode) getSchemaNode(schema, "test", "choice");
+        ChoiceNode choice = ImmutableChoiceNodeSchemaAwareBuilder.create(choice1SchemaNode)
+                .withChild(Builders.<String> leafBuilder(augumentString1SchemaNode).withValue("case1").build())
+                // This should fail, since child node belongs to different case
+                // .withChild(Builders.<String>leafBuilder(augumentString2SchemaNode).withValue("case2")
+                // .build())
+                .build();
+
+        ;
+        builder.withChild(choice);
+
+        // This should fail, child from case
+        // builder.withChild(Builders.<String>leafBuilder(augumentString1SchemaNode).withValue("case1")
+        // .build());
+    }
+
+    private static AugmentationSchema getAugmentationSchemaForChild(final ContainerSchemaNode containerNode,
+            final QName qName) {
+        for (AugmentationSchema augmentationSchema : containerNode.getAvailableAugmentations()) {
+            if (augmentationSchema.getDataChildByName(qName) != null) {
+                return augmentationSchema;
+            }
+        }
+        throw new IllegalStateException("Unable to find child augmentation in " + containerNode);
+    }
+
+    private static YangInstanceIdentifier.NodeWithValue getNodeWithValueIdentifier(final String localName,
+            final Object value) {
+        return new YangInstanceIdentifier.NodeWithValue(getQName(localName), value);
+    }
+
+    private static QName getQName(final String localName) {
+        String namespace = "namespace";
+        return new QName(URI.create(namespace), localName);
+    }
+
+    private static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String localName) {
+        return new YangInstanceIdentifier.NodeIdentifier(getQName(localName));
+    }
+
+    public static DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName,
+            final String childNodeName) {
+        for (Module module : context.getModules()) {
+            if (module.getName().equals(moduleName)) {
+                DataSchemaNode found = findChildNode(module.getChildNodes(), childNodeName);
+                Preconditions.checkState(found != null, "Unable to find %s", childNodeName);
+                return found;
+            }
+        }
+        throw new IllegalStateException("Unable to find child node " + childNodeName);
+    }
+
+    private static DataSchemaNode findChildNode(final Iterable<DataSchemaNode> children, final String name) {
+        List<DataNodeContainer> containers = Lists.newArrayList();
+
+        for (DataSchemaNode dataSchemaNode : children) {
+            if (dataSchemaNode.getQName().getLocalName().equals(name)) {
+                return dataSchemaNode;
+            }
+            if (dataSchemaNode instanceof DataNodeContainer) {
+                containers.add((DataNodeContainer) dataSchemaNode);
+            } else if (dataSchemaNode instanceof ChoiceSchemaNode) {
+                containers.addAll(((ChoiceSchemaNode) dataSchemaNode).getCases());
+            }
+        }
+
+        for (DataNodeContainer container : containers) {
+            DataSchemaNode retVal = findChildNode(container.getChildNodes(), name);
+            if (retVal != null) {
+                return retVal;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/NormalizedNodeXmlTranslationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/retest/NormalizedNodeXmlTranslationTest.java
new file mode 100644 (file)
index 0000000..8674f92
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.retest;
+
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.augmentationBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.choiceBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.Builders.containerBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+import com.google.common.base.Function;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URI;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.stream.XMLOutputFactory;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMResult;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.codec.xml.XmlDocumentUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.NormalizedDataBuilderTest;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.CollectionNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.DataContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.ListNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class NormalizedNodeXmlTranslationTest {
+    private static final Logger logger = LoggerFactory.getLogger(NormalizedNodeXmlTranslationTest.class);
+    private final SchemaContext schema;
+
+    @Parameterized.Parameters()
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+                { "augment_choice_hell.yang", "augment_choice_hell_ok.xml", augmentChoiceHell() },
+                { "augment_choice_hell.yang", "augment_choice_hell_ok2.xml", null },
+                { "augment_choice_hell.yang", "augment_choice_hell_ok3.xml", augmentChoiceHell2() },
+                { "test.yang", "simple.xml", null }, { "test.yang", "simple2.xml", null },
+                // TODO check attributes
+                { "test.yang", "simple_xml_with_attributes.xml", withAttributes() }
+        });
+    }
+
+    public static final String NAMESPACE = "urn:opendaylight:params:xml:ns:yang:controller:test";
+    private static Date revision;
+    static {
+        try {
+            revision = new SimpleDateFormat("yyyy-MM-dd").parse("2014-03-13");
+        } catch (final ParseException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    private static ContainerNode augmentChoiceHell2() {
+        final YangInstanceIdentifier.NodeIdentifier container = getNodeIdentifier("container");
+        QName augmentChoice1QName = QName.create(container.getNodeType(), "augment-choice1");
+        QName augmentChoice2QName = QName.create(augmentChoice1QName, "augment-choice2");
+        final QName containerQName = QName.create(augmentChoice1QName, "case11-choice-case-container");
+        final QName leafQName = QName.create(augmentChoice1QName, "case11-choice-case-leaf");
+
+        final YangInstanceIdentifier.AugmentationIdentifier aug1Id = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice1QName));
+        final YangInstanceIdentifier.AugmentationIdentifier aug2Id = new YangInstanceIdentifier.AugmentationIdentifier(
+                Sets.newHashSet(augmentChoice2QName));
+        final YangInstanceIdentifier.NodeIdentifier augmentChoice1Id = new YangInstanceIdentifier.NodeIdentifier(
+                augmentChoice1QName);
+        final YangInstanceIdentifier.NodeIdentifier augmentChoice2Id = new YangInstanceIdentifier.NodeIdentifier(
+                augmentChoice2QName);
+        final YangInstanceIdentifier.NodeIdentifier containerId = new YangInstanceIdentifier.NodeIdentifier(
+                containerQName);
+
+        return containerBuilder().withNodeIdentifier(container)
+                .withChild(augmentationBuilder().withNodeIdentifier(aug1Id)
+                        .withChild(choiceBuilder().withNodeIdentifier(augmentChoice1Id)
+                                .withChild(augmentationBuilder().withNodeIdentifier(aug2Id)
+                                        .withChild(choiceBuilder().withNodeIdentifier(augmentChoice2Id)
+                                                .withChild(containerBuilder().withNodeIdentifier(containerId)
+                                                        .withChild(leafNode(leafQName, "leaf-value"))
+                                                        .build())
+                                                .build())
+                                        .build())
+                                .build())
+                        .build()).build();
+    }
+
+    private static ContainerNode withAttributes() {
+        final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b = containerBuilder();
+        b.withNodeIdentifier(getNodeIdentifier("container"));
+
+        final CollectionNodeBuilder<MapEntryNode, MapNode> listBuilder = Builders.mapBuilder().withNodeIdentifier(
+                getNodeIdentifier("list"));
+
+        final Map<QName, Object> predicates = Maps.newHashMap();
+        predicates.put(getNodeIdentifier("uint32InList").getNodeType(), 3L);
+
+        final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifierWithPredicates, MapEntryNode> list1Builder = Builders
+                .mapEntryBuilder().withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeIdentifierWithPredicates(
+                                getNodeIdentifier("list").getNodeType(), predicates));
+        final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> uint32InListBuilder = Builders
+                .leafBuilder().withNodeIdentifier(getNodeIdentifier("uint32InList"));
+
+        list1Builder.withChild(uint32InListBuilder.withValue(3L).build());
+
+        listBuilder.withChild(list1Builder.build());
+        b.withChild(listBuilder.build());
+
+        final NormalizedNodeBuilder<YangInstanceIdentifier.NodeIdentifier, Object, LeafNode<Object>> booleanBuilder = Builders
+                .leafBuilder().withNodeIdentifier(getNodeIdentifier("boolean"));
+        booleanBuilder.withValue(false);
+        b.withChild(booleanBuilder.build());
+
+        final ListNodeBuilder<Object, LeafSetEntryNode<Object>> leafListBuilder = Builders.leafSetBuilder()
+                .withNodeIdentifier(getNodeIdentifier("leafList"));
+
+        final NormalizedNodeBuilder<YangInstanceIdentifier.NodeWithValue, Object, LeafSetEntryNode<Object>> leafList1Builder = Builders
+                .leafSetEntryBuilder().withNodeIdentifier(
+                        new YangInstanceIdentifier.NodeWithValue(getNodeIdentifier("leafList").getNodeType(), "a"));
+
+        leafList1Builder.withValue("a");
+
+        leafListBuilder.withChild(leafList1Builder.build());
+        b.withChild(leafListBuilder.build());
+
+        return b.build();
+    }
+
+    private static ContainerNode augmentChoiceHell() {
+
+        final DataContainerNodeBuilder<YangInstanceIdentifier.NodeIdentifier, ContainerNode> b = containerBuilder();
+        b.withNodeIdentifier(getNodeIdentifier("container"));
+
+        b.withChild(choiceBuilder()
+                .withNodeIdentifier(getNodeIdentifier("ch2"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c2Leaf")).withValue("2").build())
+                .withChild(
+                        choiceBuilder()
+                                .withNodeIdentifier(getNodeIdentifier("c2DeepChoice"))
+                                .withChild(
+                                        Builders.leafBuilder()
+                                                .withNodeIdentifier(getNodeIdentifier("c2DeepChoiceCase1Leaf2"))
+                                                .withValue("2").build()).build()).build());
+
+        b.withChild(choiceBuilder()
+                .withNodeIdentifier(getNodeIdentifier("ch3"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c3Leaf")).withValue("3").build())
+                .build());
+
+        b.withChild(augmentationBuilder()
+                .withNodeIdentifier(getAugmentIdentifier("augLeaf"))
+                .withChild(
+                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("augLeaf")).withValue("augment")
+                                .build()).build());
+
+        b.withChild(augmentationBuilder()
+                .withNodeIdentifier(getAugmentIdentifier("ch"))
+                .withChild(
+                        choiceBuilder()
+                                .withNodeIdentifier(getNodeIdentifier("ch"))
+                                .withChild(
+                                        Builders.leafBuilder().withNodeIdentifier(getNodeIdentifier("c1Leaf"))
+                                                .withValue("1").build())
+                                .withChild(
+                                        augmentationBuilder()
+                                                .withNodeIdentifier(
+                                                        getAugmentIdentifier("c1Leaf_AnotherAugment", "deepChoice"))
+                                                .withChild(
+                                                        Builders.leafBuilder()
+                                                                .withNodeIdentifier(
+                                                                        getNodeIdentifier("c1Leaf_AnotherAugment"))
+                                                                .withValue("1").build())
+                                                .withChild(
+                                                        choiceBuilder()
+                                                                .withNodeIdentifier(getNodeIdentifier("deepChoice"))
+                                                                .withChild(
+                                                                        Builders.leafBuilder()
+                                                                                .withNodeIdentifier(
+                                                                                        getNodeIdentifier("deepLeafc1"))
+                                                                                .withValue("1").build()).build())
+                                                .build()).build()).build());
+
+        return b.build();
+    }
+
+    private static YangInstanceIdentifier.NodeIdentifier getNodeIdentifier(final String localName) {
+        return new YangInstanceIdentifier.NodeIdentifier(QName.create(URI.create(NAMESPACE), revision, localName));
+    }
+
+    public static YangInstanceIdentifier.AugmentationIdentifier getAugmentIdentifier(final String... childNames) {
+        final Set<QName> qn = Sets.newHashSet();
+
+        for (final String childName : childNames) {
+            qn.add(getNodeIdentifier(childName).getNodeType());
+        }
+
+        return new YangInstanceIdentifier.AugmentationIdentifier(qn);
+    }
+
+    public NormalizedNodeXmlTranslationTest(final String yangPath, final String xmlPath,
+            final ContainerNode expectedNode) throws ReactorException {
+        schema = parseTestSchema(yangPath);
+        this.xmlPath = xmlPath;
+        this.containerNode = (ContainerSchemaNode) NormalizedDataBuilderTest.getSchemaNode(schema, "test", "container");
+        this.expectedNode = expectedNode;
+    }
+
+    private final ContainerNode expectedNode;
+    private final ContainerSchemaNode containerNode;
+    private final String xmlPath;
+
+    SchemaContext parseTestSchema(final String... yangPath) throws ReactorException {
+        return RetestUtils.parseYangStreams(getTestYangs(yangPath));
+    }
+
+    List<InputStream> getTestYangs(final String... yangPaths) {
+
+        return Lists.newArrayList(Collections2.transform(Lists.newArrayList(yangPaths),
+                new Function<String, InputStream>() {
+                    @Override
+                    public InputStream apply(final String input) {
+                        final InputStream resourceAsStream = NormalizedDataBuilderTest.class.getResourceAsStream(input);
+                        Preconditions.checkNotNull(resourceAsStream, "File %s was null", resourceAsStream);
+                        return resourceAsStream;
+                    }
+                }));
+    }
+
+    @Test
+    public void testTranslation() throws Exception {
+        final Document doc = loadDocument(xmlPath);
+
+        final ContainerNode built = DomToNormalizedNodeParserFactory
+                .getInstance(DomUtils.defaultValueCodecProvider(), schema).getContainerNodeParser()
+                .parse(Collections.singletonList(doc.getDocumentElement()), containerNode);
+
+        if (expectedNode != null) {
+            org.junit.Assert.assertEquals(expectedNode, built);
+        }
+
+        System.err.println(built);
+        logger.info("{}", built);
+
+        final Element elementNS = XmlDocumentUtils.getDocument().createElementNS(
+                containerNode.getQName().getNamespace().toString(), containerNode.getQName().getLocalName());
+        writeNormalizedNode(built, new DOMResult(elementNS), SchemaPath.create(true), schema);
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setIgnoreComments(true);
+        XMLUnit.setIgnoreAttributeOrder(true);
+        XMLUnit.setNormalize(true);
+
+        System.err.println(toString(doc.getDocumentElement()));
+        System.err.println(toString(elementNS));
+
+        final Diff diff = new Diff(XMLUnit.buildControlDocument(toString(doc.getDocumentElement())),
+                XMLUnit.buildTestDocument(toString(elementNS)));
+
+        // FIXME the comparison cannot be performed, since the qualifiers supplied by XMlUnit do not work correctly in
+        // this case
+        // We need to implement custom qualifier so that the element ordering does not mess the DIFF
+        // dd.overrideElementQualifier(new MultiLevelElementNameAndTextQualifier(100, true));
+        // assertTrue(dd.toString(), dd.similar());
+    }
+
+    static final XMLOutputFactory XML_FACTORY;
+    static {
+        XML_FACTORY = XMLOutputFactory.newFactory();
+        XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false);
+    }
+
+    private static void writeNormalizedNode(final NormalizedNode<?, ?> normalized, final DOMResult result,
+            final SchemaPath schemaPath, final SchemaContext context) throws IOException, XMLStreamException {
+        NormalizedNodeWriter normalizedNodeWriter = null;
+        NormalizedNodeStreamWriter normalizedNodeStreamWriter = null;
+        XMLStreamWriter writer = null;
+        try {
+            writer = XML_FACTORY.createXMLStreamWriter(result);
+            normalizedNodeStreamWriter = XMLStreamNormalizedNodeStreamWriter.create(writer, context, schemaPath);
+            normalizedNodeWriter = NormalizedNodeWriter.forStreamWriter(normalizedNodeStreamWriter);
+
+            normalizedNodeWriter.write(normalized);
+
+            normalizedNodeWriter.flush();
+        } finally {
+            if (normalizedNodeWriter != null) {
+                normalizedNodeWriter.close();
+            }
+            if (normalizedNodeStreamWriter != null) {
+                normalizedNodeStreamWriter.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    private static Document loadDocument(final String xmlPath) throws IOException, SAXException {
+        final InputStream resourceAsStream = NormalizedDataBuilderTest.class.getResourceAsStream(xmlPath);
+
+        final Document currentConfigElement = readXmlToDocument(resourceAsStream);
+        Preconditions.checkNotNull(currentConfigElement);
+        return currentConfigElement;
+    }
+
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    static {
+        final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        final DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDERFACTORY.newDocumentBuilder();
+        } catch (final ParserConfigurationException e) {
+            throw new RuntimeException("Failed to parse XML document", e);
+        }
+        final Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    public static String toString(final Element xml) {
+        try {
+            final Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            final StreamResult result = new StreamResult(new StringWriter());
+            final DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RetestModel.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/RetestModel.java
new file mode 100644 (file)
index 0000000..756abb0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class RetestModel {
+
+    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 QName TWO_QNAME = QName.create(TEST_QNAME, "two");
+    public static final QName THREE_QNAME = QName.create(TEST_QNAME, "three");
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return RetestModel.class.getResourceAsStream(DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() throws ReactorException {
+        return RetestUtils.parseYangStreams(Arrays.asList(getDatastoreTestInputStream()));
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug2690Fix.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug2690Fix.java
new file mode 100644 (file)
index 0000000..394a890
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import java.util.Collections;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class Retest_Bug2690Fix {
+    private static final String ODL_DATASTORE_TEST_YANG = "/odl-datastore-test.yang";
+    private SchemaContext schemaContext;
+    private RootModificationApplyOperation rootOper;
+
+    private InMemoryDataTree inMemoryDataTree;
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(schemaContext,
+                TreeType.OPERATIONAL));
+        inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+    }
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return getInputStream(ODL_DATASTORE_TEST_YANG);
+    }
+
+    private static InputStream getInputStream(final String resourceName) {
+        return TestModel.class.getResourceAsStream(ODL_DATASTORE_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() throws ReactorException {
+        return RetestUtils.parseYangStreams(Collections.singletonList(getDatastoreTestInputStream()));
+    }
+
+    @Test
+    public void testWriteMerge1() throws DataValidationFailedException {
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 1);
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 2);
+        final MapNode mapNode1 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+        final MapNode mapNode2 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.OUTER_LIST_QNAME))
+                .withChild(barEntryNode).build();
+
+        final ContainerNode cont1 = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNode1).build();
+
+        final ContainerNode cont2 = Builders.containerBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(mapNode2).build();
+
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, cont1);
+        modificationTree.merge(TestModel.TEST_PATH, cont2);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final InMemoryDataTreeSnapshot snapshotAfterTx = inMemoryDataTree.takeSnapshot();
+        final InMemoryDataTreeModification modificationAfterTx = snapshotAfterTx.newModification();
+        final Optional<NormalizedNode<?, ?>> readNode = modificationAfterTx.readNode(TestModel.OUTER_LIST_PATH);
+        assertTrue(readNode.isPresent());
+        assertEquals(2, ((NormalizedNodeContainer<?,?,?>)readNode.get()).getValue().size());
+
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug3674Test.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_Bug3674Test.java
new file mode 100644 (file)
index 0000000..05bb040
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidateNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ModificationType;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+/**
+ * BUG-3674: issuing a delete on a non-existent entry must be preserved in
+ *           DataTreeModification, but should appear as UNMODIFIED in the
+ *           resulting DataTreeCandidate.
+ */
+public class Retest_Bug3674Test {
+    private DataTree tree;
+
+    @Before
+    public void setUp() throws ReactorException {
+        tree = InMemoryDataTreeFactory.getInstance().create();
+        tree.setSchemaContext(RetestModel.createTestContext());
+
+        // Create the top-level container
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        mod.ready();
+        tree.commit(tree.prepare(mod));
+    }
+
+    @Test
+    public void testDeleteOfNonExistingNode() {
+        final DataTreeModification mod = tree.takeSnapshot().newModification();
+        mod.delete(TestModel.OUTER_LIST_PATH);
+        mod.ready();
+
+        final DataTreeCandidate candidate = tree.prepare(mod);
+        final DataTreeCandidateNode root = candidate.getRootNode();
+        assertEquals(ModificationType.UNMODIFIED, root.getModificationType());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConcurrentTreeModificationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConcurrentTreeModificationTest.java
new file mode 100644 (file)
index 0000000..2c33752
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.ConflictingModificationAppliedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Retest_ConcurrentTreeModificationTest {
+    private static final Logger LOG = LoggerFactory.getLogger(Retest_ConcurrentTreeModificationTest.class);
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .build();
+
+    private static final MapEntryNode FOO_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME) //
+                    .build()) //
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME) //
+                    .build()) //
+            .build();
+
+    private SchemaContext schemaContext;
+    private RootModificationApplyOperation rootOper;
+    private InMemoryDataTree inMemoryDataTree;
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = RetestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(schemaContext,TreeType.OPERATIONAL));
+        inMemoryDataTree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+    }
+
+    private static ContainerNode createFooTestContainerNode() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(FOO_NODE).build()).build();
+    }
+
+    private static ContainerNode createBarTestContainerNode() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(BAR_NODE).build()).build();
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+    @Test
+    public void writeWrite1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        modificationTree2.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        modificationTree1.ready();
+        modificationTree2.ready();
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeMerge1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        modificationTree2.merge(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void writeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void mergeMergeFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void writeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertFalse(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH).isPresent());
+    }
+
+    @Test
+    public void mergeMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void deleteWriteFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (final ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void mergeMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_1_PATH), MapEntryNode.class);
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void deleteWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (final Exception e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree");
+        }
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        final DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initialDataTreeModification.write(TestModel.OUTER_LIST_PATH, mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        initialDataTreeModification.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        final DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        final DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+        modificationTree1.ready();
+        modificationTree2.ready();
+
+        inMemoryDataTree.validate(modificationTree1);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            final DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (final Exception e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree");
+        }
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConfigStatementValidationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ConfigStatementValidationTest.java
new file mode 100644 (file)
index 0000000..979e020
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertNotNull;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.leafNode;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapEntryBuilder;
+import static org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes.mapNodeBuilder;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode;
+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.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+// TODO: expand these tests to catch some more obscure cases
+public class Retest_ConfigStatementValidationTest {
+    private static final Logger LOG = LoggerFactory.getLogger(Retest_ConfigStatementValidationTest.class);
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .build();
+
+    private static final MapEntryNode INNER_FOO_ENTRY_NODE =
+            ImmutableNodes.mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "foo");
+
+    private static final MapEntryNode INNER_BAR_ENTRY_NODE =
+            ImmutableNodes.mapEntryBuilder(QName.create(TestModel.TEST_QNAME, "inner-list2"), TestModel.NAME_QNAME, "foo")
+                    .withChild(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "value")).build();
+
+    private static final MapEntryNode FOO_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(INNER_FOO_ENTRY_NODE) //
+                    .build()) //
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME).withChild(INNER_BAR_ENTRY_NODE) //
+                    .build()) //
+            .build();
+
+    private SchemaContext schemaContext;
+
+    private ContainerNode createFooTestContainerNode() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(FOO_NODE).build()).build();
+    }
+
+    private ContainerNode createBarTestContainerNode() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                                .withChild(BAR_NODE).build()).build();
+    }
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = RetestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+    }
+
+    @Test(expected=SchemaValidationFailedException.class)
+    public void testOnPathFail() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree =
+                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        final YangInstanceIdentifier ii = OUTER_LIST_1_PATH.node(new YangInstanceIdentifier.NodeIdentifier(TestModel.INNER_LIST_QNAME))
+                .node(INNER_FOO_ENTRY_NODE.getIdentifier());
+        modificationTree.write(ii, INNER_FOO_ENTRY_NODE);
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected=SchemaValidationFailedException.class)
+    public void testOnDataFail() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree =
+                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected=SchemaValidationFailedException.class)
+    public void testOnDataLeafFail() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree =
+                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(TestModel.TEST_PATH, createBarTestContainerNode());
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected=SchemaValidationFailedException.class)
+    public void testOnPathCaseLeafFail() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree =
+                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(
+                QName.create(TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier.NodeIdentifier case2ContId = new YangInstanceIdentifier.NodeIdentifier(
+                QName.create(TestModel.TEST_QNAME, "case2-cont"));
+        final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id).node(case2ContId);
+        final ContainerNode case2Cont = Builders.containerBuilder().withNodeIdentifier(case2ContId)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case2-leaf1"), "leaf-value")).build();
+
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(ii, case2Cont);
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+
+    @Test(expected=SchemaValidationFailedException.class)
+    public void testOnDataCaseLeafFail() throws DataValidationFailedException {
+        final InMemoryDataTree inMemoryDataTree =
+                (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create(TreeType.CONFIGURATION);
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final YangInstanceIdentifier.NodeIdentifier choice1Id = new YangInstanceIdentifier.NodeIdentifier(
+                QName.create(TestModel.TEST_QNAME, "choice1"));
+        final YangInstanceIdentifier ii = TestModel.TEST_PATH.node(choice1Id);
+        final ChoiceNode choice1 = Builders.choiceBuilder().withNodeIdentifier(choice1Id)
+                .withChild(leafNode(QName.create(TestModel.TEST_QNAME, "case1-leaf1"), "leaf-value")).build();
+
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(ii, choice1);
+        modificationTree.ready();
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ErrorReportingTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ErrorReportingTest.java
new file mode 100644 (file)
index 0000000..236ead9
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import org.junit.Before;
+import org.junit.Test;
+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.ModifiedNodeDoesNotExistException;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+public class Retest_ErrorReportingTest {
+
+    private InMemoryDataTree tree;
+
+    @Before
+    public void setup() throws ReactorException {
+        tree = (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create();
+        tree.setSchemaContext(RetestModel.createTestContext());
+    }
+
+    @Test
+    public void writeWithoutParentExisting() {
+        InMemoryDataTreeModification modification = tree.takeSnapshot().newModification();
+        // We write node without creating parent
+        modification.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        modification.ready();
+        try {
+            tree.validate(modification);
+            fail("ModifiedNodeDoesNotExistException should be raised");
+        } catch (ModifiedNodeDoesNotExistException e) {
+            assertEquals(TestModel.TEST_PATH, e.getPath());
+        } catch (DataValidationFailedException e) {
+            fail("ModifiedNodeDoesNotExistException expected");
+        }
+    }
+
+    @Test
+    public void parentConcurrentlyDeletedExisting() {
+        InMemoryDataTreeModification initial = tree.takeSnapshot().newModification();
+        // We write node without creating parent
+        initial.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        initial.ready();
+        // We commit transaction
+        tree.commit(tree.prepare(initial));
+
+        InMemoryDataTreeModification writeTx = tree.takeSnapshot().newModification();
+        InMemoryDataTreeModification deleteTx = tree.takeSnapshot().newModification();
+        deleteTx.delete(TestModel.TEST_PATH);
+        deleteTx.ready();
+        // We commit delete modification
+        tree.commit(tree.prepare(deleteTx));
+
+        writeTx.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+        writeTx.ready();
+        try {
+            tree.validate(writeTx);
+            fail("ConflictingModificationAppliedException should be raised");
+        } catch (ConflictingModificationAppliedException e) {
+            assertEquals(TestModel.TEST_PATH, e.getPath());
+        } catch (DataValidationFailedException e) {
+            fail("ConflictingModificationAppliedException expected");
+        }
+
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ListConstraintsValidation.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ListConstraintsValidation.java
new file mode 100644 (file)
index 0000000..60df04d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import com.google.common.base.Optional;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNodeContainer;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeCandidate;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataValidationFailedException;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+import org.opendaylight.yangtools.yang.data.impl.RetestUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
+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.ImmutableUnkeyedListEntryNodeBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableUnkeyedListNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Retest_ListConstraintsValidation {
+    private static final Logger LOG = LoggerFactory.getLogger(Retest_ListConstraintsValidation.class);
+
+    private static final String CONSTRAINTS_VALIDATION_TEST_YANG = "/list-constraints-validation-test-model.yang";
+    private SchemaContext schemaContext;
+    private RootModificationApplyOperation rootOper;
+
+    private InMemoryDataTree inMemoryDataTree;
+
+    private static final QName MASTER_CONTAINER_QNAME = QName.create(
+            "urn:opendaylight:params:xml:ns:yang:list-constraints-validation-test-model", "2015-02-02",
+            "master-container");
+    private static final QName MIN_MAX_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-list");
+    private static final QName MIN_MAX_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-key-leaf");
+    private static final QName UNBOUNDED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-list");
+    private static final QName UNBOUNDED_KEY_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-key-leaf");
+    private static final QName MIN_MAX_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "min-max-leaf-list");
+    private static final QName UNBOUNDED_LEAF_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unbounded-leaf-list");
+    private static final QName UNKEYED_LIST_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-list");
+    private static final QName UNKEYED_LEAF_QNAME = QName.create(MASTER_CONTAINER_QNAME, "unkeyed-leaf");
+
+    private static final YangInstanceIdentifier MASTER_CONTAINER_PATH = YangInstanceIdentifier
+            .of(MASTER_CONTAINER_QNAME);
+    private static final YangInstanceIdentifier MIN_MAX_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNBOUNDED_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LIST_QNAME).build();
+    private static final YangInstanceIdentifier MIN_MAX_LEAF_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(MIN_MAX_LEAF_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNBOUNDED_LEAF_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNBOUNDED_LEAF_LIST_QNAME).build();
+    private static final YangInstanceIdentifier UNKEYED_LIST_PATH = YangInstanceIdentifier
+            .builder(MASTER_CONTAINER_PATH).node(UNKEYED_LIST_QNAME).build();
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(schemaContext, TreeType.OPERATIONAL));
+        inMemoryDataTree =  (InMemoryDataTree) InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        final InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+        final DataTreeModification modificationTree = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
+        modificationTree.ready();
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(modificationTree));
+    }
+
+    public static final InputStream getDatastoreTestInputStream() {
+        return getInputStream(CONSTRAINTS_VALIDATION_TEST_YANG);
+    }
+
+    private static InputStream getInputStream(final String resourceName) {
+        return TestModel.class.getResourceAsStream(CONSTRAINTS_VALIDATION_TEST_YANG);
+    }
+
+    public static SchemaContext createTestContext() throws ReactorException {
+        return RetestUtils.parseYangStreams(Collections.singletonList(getDatastoreTestInputStream()));
+    }
+
+    @Test
+    public void minMaxListTestPass() throws DataValidationFailedException {
+
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo");
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
+        final MapNode mapNode1 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+        final MapNode mapNode2 = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(barEntryNode).build();
+
+        final InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(MIN_MAX_LIST_PATH, mapNode1);
+        modificationTree.merge(MIN_MAX_LIST_PATH, mapNode2);
+        // TODO: check why write and then merge on list commits only "bar" child
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 2);
+    }
+
+    @Test(expected=DataValidationFailedException.class)
+    public void minMaxListFail() throws DataValidationFailedException {
+        InMemoryDataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final MapEntryNode fooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "foo");
+        final MapEntryNode barEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "bar");
+        final MapEntryNode gooEntryNode = ImmutableNodes.mapEntry(MIN_MAX_LIST_QNAME, MIN_MAX_KEY_LEAF_QNAME, "goo");
+        final MapNode mapNode = ImmutableNodes.mapNodeBuilder()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LIST_QNAME))
+                .withChild(fooEntryNode).build();
+
+        final YangInstanceIdentifier fooPath = MIN_MAX_LIST_PATH.node(fooEntryNode.getIdentifier());
+        final YangInstanceIdentifier barPath = MIN_MAX_LIST_PATH.node(barEntryNode.getIdentifier());
+        final YangInstanceIdentifier gooPath = MIN_MAX_LIST_PATH.node(gooEntryNode.getIdentifier());
+
+        modificationTree.write(MIN_MAX_LIST_PATH, mapNode);
+        modificationTree.merge(barPath, barEntryNode);
+        modificationTree.write(gooPath, gooEntryNode);
+        modificationTree.delete(gooPath);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        InMemoryDataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        Optional<NormalizedNode<?, ?>> minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 2);
+
+        modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+        modificationTree.write(gooPath, gooEntryNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        minMaxListRead = snapshotAfterCommit.readNode(MIN_MAX_LIST_PATH);
+        assertTrue(minMaxListRead.isPresent());
+        assertTrue(((NormalizedNodeContainer<?, ?, ?>) minMaxListRead.get()).getValue().size() == 3);
+
+        modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        modificationTree.delete(gooPath);
+        modificationTree.delete(fooPath);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+    }
+
+    @Test
+    public void minMaxLeafListPass() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final YangInstanceIdentifier.NodeWithValue barPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "bar");
+        final YangInstanceIdentifier.NodeWithValue gooPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "goo");
+
+        final LeafSetEntryNode<Object> barLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(barPath)
+                .withValue("bar").build();
+        final LeafSetEntryNode<Object> gooLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(gooPath)
+                .withValue("goo").build();
+
+        final LeafSetNode<Object> fooLeafSetNode = ImmutableLeafSetNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
+                .withChildValue("foo").build();
+
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
+        modificationTree.delete(MIN_MAX_LEAF_LIST_PATH.node(gooPath));
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> masterContainer = snapshotAfterCommit.readNode(MASTER_CONTAINER_PATH);
+        assertTrue(masterContainer.isPresent());
+        final Optional<NormalizedNodeContainer<?, ?, ?>> leafList = ((NormalizedNodeContainer) masterContainer.get()).getChild(
+                new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME));
+        assertTrue(leafList.isPresent());
+        assertTrue(leafList.get().getValue().size() == 2);
+    }
+
+    @Test(expected=DataValidationFailedException.class)
+    public void minMaxLeafListFail() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final YangInstanceIdentifier.NodeWithValue fooPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "foo");
+        final YangInstanceIdentifier.NodeWithValue barPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "bar");
+        final YangInstanceIdentifier.NodeWithValue gooPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "goo");
+        final YangInstanceIdentifier.NodeWithValue fuuPath = new YangInstanceIdentifier.NodeWithValue(MIN_MAX_LIST_QNAME, "fuu");
+
+        final LeafSetEntryNode<Object> barLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(barPath)
+                .withValue("bar").build();
+        final LeafSetEntryNode<Object> gooLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(gooPath)
+                .withValue("goo").build();
+        final LeafSetEntryNode<Object> fuuLeafSetEntry = ImmutableLeafSetEntryNodeBuilder.create()
+                .withNodeIdentifier(fuuPath)
+                .withValue("fuu").build();
+
+        final LeafSetNode<Object> fooLeafSetNode = ImmutableLeafSetNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(MIN_MAX_LEAF_LIST_QNAME))
+                .withChildValue("foo").build();
+
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH, fooLeafSetNode);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(barPath), barLeafSetEntry);
+        modificationTree.merge(MIN_MAX_LEAF_LIST_PATH.node(gooPath), gooLeafSetEntry);
+        modificationTree.write(MIN_MAX_LEAF_LIST_PATH.node(fuuPath), fuuLeafSetEntry);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+    }
+
+    @Test
+    public void unkeyedListTestPass() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final UnkeyedListEntryNode foo = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo")).build();
+        final List<UnkeyedListEntryNode> unkeyedEntries = new ArrayList<>();
+        unkeyedEntries.add(foo);
+        final UnkeyedListNode unkeyedListNode = ImmutableUnkeyedListNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LIST_QNAME))
+                .withValue(unkeyedEntries).build();
+
+        modificationTree.write(MASTER_CONTAINER_PATH, ImmutableNodes.containerNode(MASTER_CONTAINER_QNAME));
+        modificationTree.merge(UNKEYED_LIST_PATH, unkeyedListNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+        final DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree);
+        inMemoryDataTree.commit(prepare1);
+
+        final InMemoryDataTreeSnapshot snapshotAfterCommit = inMemoryDataTree.takeSnapshot();
+        final Optional<NormalizedNode<?, ?>> unkeyedListRead = snapshotAfterCommit.readNode(UNKEYED_LIST_PATH);
+        assertTrue(unkeyedListRead.isPresent());
+        assertTrue(((UnkeyedListNode) unkeyedListRead.get()).getSize() == 1);
+    }
+
+    @Test(expected=DataValidationFailedException.class)
+    public void unkeyedListTestFail() throws DataValidationFailedException {
+        final DataTreeModification modificationTree = inMemoryDataTree.takeSnapshot().newModification();
+
+        final UnkeyedListEntryNode foo = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "foo")).build();
+        final UnkeyedListEntryNode bar = ImmutableUnkeyedListEntryNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LEAF_QNAME))
+                .withChild(ImmutableNodes.leafNode(UNKEYED_LEAF_QNAME, "bar")).build();
+        final List<UnkeyedListEntryNode> unkeyedEntries = new ArrayList<>();
+        unkeyedEntries.add(foo);
+        unkeyedEntries.add(bar);
+        final UnkeyedListNode unkeyedListNode = ImmutableUnkeyedListNodeBuilder.create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(UNKEYED_LIST_QNAME))
+                .withValue(unkeyedEntries).build();
+
+        modificationTree.write(UNKEYED_LIST_PATH, unkeyedListNode);
+        modificationTree.ready();
+
+        inMemoryDataTree.validate(modificationTree);
+    }
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ModificationMetadataTreeTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_ModificationMetadataTreeTest.java
new file mode 100644 (file)
index 0000000..2aaf112
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+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;
+
+import com.google.common.base.Optional;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+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.MapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTree;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+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.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+
+/**
+ *
+ * Schema structure of document is
+ *
+ * <pre>
+ * container root { 
+ *      list list-a {
+ *              key leaf-a;
+ *              leaf leaf-a;
+ *              choice choice-a {
+ *                      case one {
+ *                              leaf one;
+ *                      }
+ *                      case two-three {
+ *                              leaf two;
+ *                              leaf three;
+ *                      }
+ *              }
+ *              list list-b {
+ *                      key leaf-b;
+ *                      leaf leaf-b;
+ *              }
+ *      }
+ * }
+ * </pre>
+ *
+ */
+public class Retest_ModificationMetadataTreeTest {
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+    private static final String TWO_ONE_NAME = "one";
+    private static final String TWO_TWO_NAME = "two";
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_PATH = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+            .node(TestModel.INNER_LIST_QNAME) //
+            .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME) //
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_VALUE_PATH = YangInstanceIdentifier.builder(TWO_TWO_PATH)
+            .node(TestModel.VALUE_QNAME) //
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME) //
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_ONE_NAME)) //
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME,TestModel.NAME_QNAME, TWO_TWO_NAME)) //
+                    .build()) //
+                    .build();
+
+    private SchemaContext schemaContext;
+    private RootModificationApplyOperation rootOper;
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = RetestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(schemaContext, TreeType.OPERATIONAL));
+    }
+
+    /**
+     * Returns a test document
+     *
+     * <pre>
+     * test
+     *     outer-list
+     *          id 1
+     *     outer-list
+     *          id 2
+     *          inner-list
+     *                  name "one"
+     *          inner-list
+     *                  name "two"
+     *
+     * </pre>
+     *
+     * @return
+     */
+    public NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(schemaContext.getQName()))
+                .withChild(createTestContainer()).build();
+
+    }
+
+    private static ContainerNode createTestContainer() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                        .withChild(mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID))
+                        .withChild(BAR_NODE).build()).build();
+    }
+
+    @Test
+    public void basicReadWrites() {
+        final DataTreeModification modificationTree = new InMemoryDataTreeModification(new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper),
+                rootOper);
+        final Optional<NormalizedNode<?, ?>> originalBarNode = modificationTree.readNode(OUTER_LIST_2_PATH);
+        assertTrue(originalBarNode.isPresent());
+        assertSame(BAR_NODE, originalBarNode.get());
+
+        // writes node to /outer-list/1/inner_list/two/value
+        modificationTree.write(TWO_TWO_VALUE_PATH, ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "test"));
+
+        // reads node to /outer-list/1/inner_list/two/value
+        // and checks if node is already present
+        final Optional<NormalizedNode<?, ?>> barTwoCModified = modificationTree.readNode(TWO_TWO_VALUE_PATH);
+        assertTrue(barTwoCModified.isPresent());
+        assertEquals(ImmutableNodes.leafNode(TestModel.VALUE_QNAME, "test"), barTwoCModified.get());
+
+        // delete node to /outer-list/1/inner_list/two/value
+        modificationTree.delete(TWO_TWO_VALUE_PATH);
+        final Optional<NormalizedNode<?, ?>> barTwoCAfterDelete = modificationTree.readNode(TWO_TWO_VALUE_PATH);
+        assertFalse(barTwoCAfterDelete.isPresent());
+    }
+
+
+    public DataTreeModification createEmptyModificationTree() {
+        /**
+         * Creates empty Snapshot with associated schema context.
+         */
+        final DataTree t = InMemoryDataTreeFactory.getInstance().create();
+        t.setSchemaContext(schemaContext);
+
+        /**
+         *
+         * Creates Mutable Data Tree based on provided snapshot and schema
+         * context.
+         *
+         */
+        return t.takeSnapshot().newModification();
+    }
+
+    @Test
+    public void createFromEmptyState() {
+
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        /**
+         * Writes empty container node to /test
+         *
+         */
+        modificationTree.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+
+        /**
+         * Writes empty list node to /test/outer-list
+         */
+        modificationTree.write(TestModel.OUTER_LIST_PATH, ImmutableNodes.mapNodeBuilder(TestModel.OUTER_LIST_QNAME).build());
+
+        /**
+         * Reads list node from /test/outer-list
+         */
+        final Optional<NormalizedNode<?, ?>> potentialOuterList = modificationTree.readNode(TestModel.OUTER_LIST_PATH);
+        assertTrue(potentialOuterList.isPresent());
+
+        /**
+         * Reads container node from /test and verifies that it contains test
+         * node
+         */
+        final Optional<NormalizedNode<?, ?>> potentialTest = modificationTree.readNode(TestModel.TEST_PATH);
+        final ContainerNode containerTest = assertPresentAndType(potentialTest, ContainerNode.class);
+
+        /**
+         *
+         * Gets list from returned snapshot of /test and verifies it contains
+         * outer-list
+         *
+         */
+        assertPresentAndType(containerTest.getChild(new NodeIdentifier(TestModel.OUTER_LIST_QNAME)), MapNode.class);
+
+    }
+
+    @Test
+    public void writeSubtreeReadChildren() {
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        modificationTree.write(TestModel.TEST_PATH, createTestContainer());
+        final Optional<NormalizedNode<?, ?>> potential = modificationTree.readNode(TWO_TWO_PATH);
+        assertPresentAndType(potential, MapEntryNode.class);
+    }
+
+    @Test
+    public void writeSubtreeDeleteChildren() {
+        final DataTreeModification modificationTree = createEmptyModificationTree();
+        modificationTree.write(TestModel.TEST_PATH, createTestContainer());
+
+        // We verify data are present
+        final Optional<NormalizedNode<?, ?>> potentialBeforeDelete = modificationTree.readNode(TWO_TWO_PATH);
+        assertPresentAndType(potentialBeforeDelete, MapEntryNode.class);
+
+        modificationTree.delete(TWO_TWO_PATH);
+        final Optional<NormalizedNode<?, ?>> potentialAfterDelete = modificationTree.readNode(TWO_TWO_PATH);
+        assertFalse(potentialAfterDelete.isPresent());
+
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+}
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_StoreTreeNodesTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/Retest_StoreTreeNodesTest.java
new file mode 100644 (file)
index 0000000..baef1ac
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+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;
+
+import com.google.common.base.Optional;
+
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.StoreTreeNodes;
+import org.opendaylight.yangtools.yang.data.api.schema.tree.TreeType;
+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.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Retest_StoreTreeNodesTest {
+    private static final Logger LOG = LoggerFactory.getLogger(Retest_StoreTreeNodesTest.class);
+
+    private static final Short ONE_ID = 1;
+    private static final Short TWO_ID = 2;
+    private static final String TWO_ONE_NAME = "one";
+    private static final String TWO_TWO_NAME = "two";
+
+    private static final YangInstanceIdentifier OUTER_LIST_1_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier OUTER_LIST_2_PATH = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+            .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .build();
+
+    private static final YangInstanceIdentifier TWO_TWO_PATH = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+            .node(TestModel.INNER_LIST_QNAME) //
+            .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME) //
+            .build();
+
+    private static final MapEntryNode BAR_NODE = mapEntryBuilder(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, TWO_ID) //
+            .withChild(mapNodeBuilder(TestModel.INNER_LIST_QNAME) //
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_ONE_NAME)) //
+                    .withChild(mapEntry(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, TWO_TWO_NAME)) //
+                    .build()) //
+                    .build();
+
+    private SchemaContext schemaContext;
+    private RootModificationApplyOperation rootOper;
+
+    @Before
+    public void prepare() throws ReactorException {
+        schemaContext = RetestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(schemaContext, TreeType.OPERATIONAL));
+    }
+
+    public NormalizedNode<?, ?> createDocumentOne() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(schemaContext.getQName()))
+                .withChild(createTestContainer()).build();
+
+    }
+
+    private static ContainerNode createTestContainer() {
+        return ImmutableContainerNodeBuilder
+                .create()
+                .withNodeIdentifier(new YangInstanceIdentifier.NodeIdentifier(TestModel.TEST_QNAME))
+                .withChild(
+                        mapNodeBuilder(TestModel.OUTER_LIST_QNAME)
+                        .withChild(mapEntry(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, ONE_ID))
+                        .withChild(BAR_NODE).build()).build();
+    }
+
+    private static <T> T assertPresentAndType(final Optional<?> potential, final Class<T> type) {
+        assertNotNull(potential);
+        assertTrue(potential.isPresent());
+        assertTrue(type.isInstance(potential.get()));
+        return type.cast(potential.get());
+    }
+
+    @Test
+    public void findNodeTestNodeFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<TreeNode> node = StoreTreeNodes.findNode(rootNode, OUTER_LIST_1_PATH);
+        assertPresentAndType(node, TreeNode.class);
+    }
+
+    @Test
+    public void findNodeTestNodeNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) //
+                .build();
+        final Optional<TreeNode> node = StoreTreeNodes.findNode(rootNode, outerList1InvalidPath);
+        assertFalse(node.isPresent());
+    }
+
+    @Test
+    public void findNodeCheckedTestNodeFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        TreeNode foundNode = null;
+        try {
+            foundNode = StoreTreeNodes.findNodeChecked(rootNode, OUTER_LIST_1_PATH);
+        } catch (final IllegalArgumentException e) {
+            fail("Illegal argument exception was thrown and should not have been" + e.getMessage());
+        }
+        assertNotNull(foundNode);
+    }
+
+    @Test
+    public void findNodeCheckedTestNodeNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerList1InvalidPath = YangInstanceIdentifier.builder(TestModel.OUTER_LIST_PATH)
+                .nodeWithKey(TestModel.OUTER_LIST_QNAME, TestModel.ID_QNAME, 3) //
+                .build();
+        try {
+            StoreTreeNodes.findNodeChecked(rootNode, outerList1InvalidPath);
+            fail("Illegal argument exception should have been thrown");
+        } catch (final IllegalArgumentException e) {
+            LOG.debug("Illegal argument exception was thrown as expected: '{}' - '{}'", e.getClass(), e.getMessage());
+        }
+    }
+
+    @Test
+    public void findClosestOrFirstMatchTestNodeExists() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<TreeNode> expectedNode = StoreTreeNodes.findNode(rootNode, TWO_TWO_PATH);
+        assertPresentAndType(expectedNode, TreeNode.class);
+        final Map.Entry<YangInstanceIdentifier, TreeNode> actualNode = StoreTreeNodes.findClosest(rootNode, TWO_TWO_PATH);
+        assertEquals("Expected node and actual node are not the same", expectedNode.get(), actualNode.getValue());
+    }
+
+    @Test
+    public void findClosestOrFirstMatchTestNodeDoesNotExist() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final YangInstanceIdentifier outerListInnerListPath = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+                .node(TestModel.INNER_LIST_QNAME)
+                .build();
+        final YangInstanceIdentifier twoTwoInvalidPath = YangInstanceIdentifier.builder(OUTER_LIST_2_PATH)
+                .node(TestModel.INNER_LIST_QNAME) //
+                .nodeWithKey(TestModel.INNER_LIST_QNAME, TestModel.NAME_QNAME, "three") //
+                .build();
+        final Optional<TreeNode> expectedNode = StoreTreeNodes.findNode(rootNode, outerListInnerListPath);
+        assertPresentAndType(expectedNode, TreeNode.class);
+        final Map.Entry<YangInstanceIdentifier, TreeNode> actualNode = StoreTreeNodes.findClosest(rootNode, twoTwoInvalidPath);
+        assertEquals("Expected node and actual node are not the same", expectedNode.get(), actualNode.getValue());
+    }
+
+    @Test
+    public void getChildTestChildFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<TreeNode> node = StoreTreeNodes.getChild(Optional.fromNullable(rootNode),
+                TestModel.TEST_PATH.getLastPathArgument());
+        assertPresentAndType(node, TreeNode.class);
+    }
+
+    @Test
+    public void getChildTestChildNotFound() {
+        final InMemoryDataTreeSnapshot inMemoryDataTreeSnapshot = new InMemoryDataTreeSnapshot(schemaContext,
+                TreeNodeFactory.createTreeNodeRecursively(createDocumentOne(), Version.initial()), rootOper);
+        final TreeNode rootNode = inMemoryDataTreeSnapshot.getRootNode();
+        final Optional<TreeNode> node = StoreTreeNodes.getChild(Optional.fromNullable(rootNode),
+                TestModel.OUTER_LIST_PATH.getLastPathArgument());
+        assertFalse(node.isPresent());
+    }
+}
diff --git a/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/RetestUtils.java b/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/RetestUtils.java
new file mode 100644 (file)
index 0000000..d4f6a38
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.yangtools.yang.data.operations.retest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.Collection;
+
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementStreamSource;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+
+public class RetestUtils {
+
+    private RetestUtils() {
+        throw new UnsupportedOperationException("Utility class");
+    }
+
+    public static SchemaContext parseYangSources(StatementStreamSource... sources) throws SourceException,
+            ReactorException {
+
+        CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        reactor.addSources(sources);
+
+        return reactor.buildEffective();
+    }
+
+    public static SchemaContext parseYangSources(File... files) throws SourceException, ReactorException,
+            FileNotFoundException {
+
+        StatementStreamSource[] sources = new StatementStreamSource[files.length];
+
+        for (int i = 0; i < files.length; i++) {
+            sources[i] = new YangStatementSourceImpl(new FileInputStream(files[i]));
+        }
+
+        return parseYangSources(sources);
+    }
+
+    public static SchemaContext parseYangSources(Collection<File> files) throws SourceException, ReactorException,
+            FileNotFoundException {
+        return parseYangSources(files.toArray(new File[files.size()]));
+    }
+}
diff --git a/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsNegativeTest.java b/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsNegativeTest.java
new file mode 100644 (file)
index 0000000..2af59f2
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.operations.retest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import org.opendaylight.yangtools.yang.data.operations.DataOperations;
+
+import org.opendaylight.yangtools.yang.data.operations.DataModificationException;
+import java.util.Arrays;
+import java.util.Collection;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class YangDataOperationsNegativeTest extends YangDataOperationsTest{
+
+    private static final String XML_NEG_FOLDER_NAME = "/xmls-negative";
+    private final Class<? extends DataModificationException> expected;
+
+    @Parameterized.Parameters()
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+
+                // Container
+                { "/containerTest_noneContainerActualMissing", DataModificationException.DataMissingException.class },
+                { "/containerTest_createContainerActualPresent", DataModificationException.DataExistsException.class },
+                { "/containerTest_deleteContainerActualMissing", DataModificationException.DataMissingException.class },
+                // List
+                { "/listTest_createListActualPresent", DataModificationException.DataExistsException.class },
+                { "/listTest_deleteListActualMissing", DataModificationException.DataMissingException.class },
+                { "/listTest_noneListActualMissing", DataModificationException.DataMissingException.class },
+                // Leaf
+                { "/leafTest_createLeafActualPresent", DataModificationException.DataExistsException.class },
+                { "/leafTest_deleteLeafActualMissing", DataModificationException.DataMissingException.class },
+                // Leaf list
+                { "/leafListTest_createLeafActualPresent", DataModificationException.DataExistsException.class },
+                { "/leafListTest_deleteLeafActualMissing", DataModificationException.DataMissingException.class },
+        });
+    }
+
+    public YangDataOperationsNegativeTest(String testDir, Class<? extends DataModificationException> e) throws Exception {
+        super(testDir);
+        this.expected = e;
+    }
+
+    @Override
+    protected String getXmlFolderName() {
+        return XML_NEG_FOLDER_NAME;
+    }
+
+    @Test
+    public void testModification() throws Exception {
+        try {
+            DataOperations.modify(containerNode,
+                    currentConfig.orNull(), modification.orNull(), modifyAction);
+            fail("Negative test for " + testDirName + " should have failed");
+        } catch (Exception e) {
+            assertEquals(e.getClass(), expected);
+        }
+    }
+}
diff --git a/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsTest.java b/yang/yang-data-operations/src/test/java/org/opendaylight/yangtools/yang/data/operations/retest/YangDataOperationsTest.java
new file mode 100644 (file)
index 0000000..4367c77
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.data.operations.retest;
+
+import static org.junit.Assert.assertNull;
+
+import com.google.common.base.Charsets;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.io.Files;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import javax.activation.UnsupportedDataTypeException;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.opendaylight.yangtools.yang.data.api.ModifyAction;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory;
+import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer.DomFromNormalizedNodeSerializerFactory;
+import org.opendaylight.yangtools.yang.data.operations.DataOperations;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+@RunWith(Parameterized.class)
+public class YangDataOperationsTest {
+
+    public static final String CURRENT_XML_NAME = "/current.xml";
+    public static final String MODIFICATION_XML_NAME = "/merge.xml";
+    private static final String XML_FOLDER_NAME = "/xmls";
+    public static final String RESULT_XML_NAME = "/result.xml";
+    private static final Object OPERATION_XML_NAME = "/defaultOperation.txt";
+
+    protected final ContainerSchemaNode containerNode;
+    protected final String testDirName;
+    protected final Optional<ContainerNode> currentConfig;
+    protected final Optional<ContainerNode> modification;
+    protected final ModifyAction modifyAction;
+    private final SchemaContext schema;
+
+    @Parameterized.Parameters()
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {
+                // Container
+                { "/containerTest_createContainer" },
+                { "/containerTest_deleteContainer" },
+                { "/containerTest_innerContainerContainer" },
+                { "/containerTest_innerLeavesBaseOperationsContainer" },
+                { "/containerTest_noneContainer" },
+                { "/containerTest_removeContainer"},
+                { "/containerTest_replaceContainer"},
+                { "/containerTest_choiceActualModificationSameCase"},
+                { "/containerTest_choiceActualModificationDifferentCases"},
+                { "/containerTest_choiceActualOneCaseModificationOtherCase"},
+                //            LeafList
+                { "/leafListTest" },
+                // List
+                { "/listTest" },
+                // Additional
+                {"/none_NoChange"},
+                {"/listTest_alterInnerValue"}
+        });
+    }
+
+    public YangDataOperationsTest(final String testDir) throws Exception {
+        schema = parseTestSchema();
+        containerNode = (ContainerSchemaNode) getSchemaNode(schema, "test", "container");
+        this.testDirName = testDir;
+
+        currentConfig = loadXmlToCompositeNode(getXmlFolderName() + testDirName + CURRENT_XML_NAME);
+        modification = loadXmlToCompositeNode(getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
+        Preconditions.checkState(modification.isPresent(), "Modification xml has to be present under "
+                + getXmlFolderName() + testDirName + MODIFICATION_XML_NAME);
+
+        modifyAction = loadModifyAction(getXmlFolderName() + testDirName + OPERATION_XML_NAME);
+    }
+
+    protected String getXmlFolderName() {
+        return XML_FOLDER_NAME;
+    }
+
+    // TODO unite testing resources e.g. schemas with yang-data-impl
+    // TODO create extract common testing infrastructure from this and yang-data-impl e.g. xml dom handling
+
+    @Test
+    public void testModification() throws Exception {
+
+        Optional<ContainerNode> result = DataOperations.modify(containerNode,
+                currentConfig.orNull(), modification.orNull(), modifyAction);
+
+        String expectedResultXmlPath = getXmlFolderName() + testDirName + RESULT_XML_NAME;
+        Optional<ContainerNode> expectedResult = loadXmlToCompositeNode(expectedResultXmlPath);
+
+        if (result.isPresent()) {
+            verifyModificationResult(result, expectedResult);
+        } else {
+            assertNull("Result of modification is empty node, result xml should not be present "
+                    + expectedResultXmlPath, getClass().getResourceAsStream(expectedResultXmlPath));
+        }
+
+    }
+
+    private ModifyAction loadModifyAction(final String path) throws Exception {
+        URL resource = getClass().getResource(path);
+        if (resource == null) {
+            return ModifyAction.MERGE;
+        }
+
+        return ModifyAction.fromXmlValue(Files.toString(new File(resource.toURI()), Charsets.UTF_8).trim());
+    }
+
+    private void verifyModificationResult(final Optional<ContainerNode> result, final Optional<ContainerNode> expectedResult)
+            throws UnsupportedDataTypeException {
+        Assert.assertEquals(
+                String.format(
+                        "Test result %n %s %n Expected %n %s %n",
+                        toString(toDom(result.get())),
+                        toString(toDom(expectedResult.get()))), expectedResult.get(), result.get());
+    }
+
+    private Element toDom(final ContainerNode container) {
+        Iterable<Element> a =
+                DomFromNormalizedNodeSerializerFactory.getInstance(newDocument(), DomUtils.defaultValueCodecProvider())
+                .getContainerNodeSerializer().serialize(containerNode, container);
+        return a.iterator().next();
+    }
+
+    private Document newDocument() {
+        try {
+            return BUILDERFACTORY.newDocumentBuilder().newDocument();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException("Failed to parse XML document", e);
+        }
+    }
+
+    private Optional<ContainerNode> loadXmlToCompositeNode(final String xmlPath) throws IOException, SAXException {
+        InputStream resourceAsStream = getClass().getResourceAsStream(xmlPath);
+        if (resourceAsStream == null) {
+            return Optional.absent();
+        }
+
+        Document currentConfigElement = readXmlToDocument(resourceAsStream);
+        Preconditions.checkNotNull(currentConfigElement);
+
+        return Optional.fromNullable(DomToNormalizedNodeParserFactory.getInstance(DomUtils.defaultValueCodecProvider(), schema).getContainerNodeParser().parse(
+                Collections.singletonList(currentConfigElement.getDocumentElement()), containerNode));
+    }
+
+    SchemaContext parseTestSchema() throws URISyntaxException, FileNotFoundException, ReactorException {
+        File testYang = new File(getClass().getResource("/schemas/test.yang").toURI());
+        return RetestUtils.parseYangSources(testYang);
+    }
+
+    DataSchemaNode getSchemaNode(final SchemaContext context, final String moduleName, final String childNodeName) {
+        for (Module module : context.getModules()) {
+            if (module.getName().equals(moduleName)) {
+                for (DataSchemaNode dataSchemaNode : module.getChildNodes()) {
+                    if (dataSchemaNode.getQName().getLocalName().equals(childNodeName)) {
+                        return dataSchemaNode;
+                    }
+                }
+            }
+        }
+
+        throw new IllegalStateException("Unable to find child node " + childNodeName);
+    }
+
+    private static final DocumentBuilderFactory BUILDERFACTORY;
+
+    static {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        factory.setCoalescing(true);
+        factory.setIgnoringElementContentWhitespace(true);
+        factory.setIgnoringComments(true);
+        BUILDERFACTORY = factory;
+    }
+
+    private Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException {
+        DocumentBuilder dBuilder;
+        try {
+            dBuilder = BUILDERFACTORY.newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new RuntimeException("Failed to parse XML document", e);
+        }
+        Document doc = dBuilder.parse(xmlContent);
+
+        doc.getDocumentElement().normalize();
+        return doc;
+    }
+
+    public static String toString(final Element xml) {
+        try {
+            Transformer transformer = TransformerFactory.newInstance().newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            StreamResult result = new StreamResult(new StringWriter());
+            DOMSource source = new DOMSource(xml);
+            transformer.transform(source, result);
+
+            return result.getWriter().toString();
+        } catch (IllegalArgumentException | TransformerFactoryConfigurationError | TransformerException e) {
+            throw new RuntimeException("Unable to serialize xml element " + xml, e);
+        }
+    }
+
+}
index 7bdafe0074111e51e0bdacc320aed3f9e8e7df0d..961ab7c0067e01afd2fb39dcc76ae4b8277fe87e 100644 (file)
@@ -70,7 +70,8 @@ public class YangToSourcesPluginTestIT {
         } catch (VerificationException e) {
             assertVerificationException(
                     e,
-                    "org.opendaylight.yangtools.yang.parser.util.YangValidationException: Not existing module imported:unknownDep:2013-02-27 by:private:2013-02-27");
+                    "org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException: Imported module " +
+                    "[ModuleIdentifierImpl{name='unknownDep', namespace=null, revision=Wed Feb 27 00:00:00 ");
             return;
         }
 
index b3612ca82c3e74ebcb6141b25bb35fbdca6fd4c9..f37316ceeb84c11e5ebbf77ce706f592040a3e89 100644 (file)
@@ -25,7 +25,6 @@ import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.project.MavenProject;
 import org.apache.maven.repository.RepositorySystem;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
 import org.opendaylight.yangtools.yang2sources.spi.BasicCodeGenerator;
 import org.sonatype.plexus.build.incremental.BuildContext;
@@ -35,7 +34,7 @@ import org.sonatype.plexus.build.incremental.BuildContext;
  * {@link BasicCodeGenerator}s. Steps of this process:
  * <ol>
  * <li>List yang files from {@link #yangFilesRootDir}</li>
- * <li>Process yang files using {@link YangParserImpl}</li>
+ * <li>Process yang files using Yang Parser</li>
  * <li>For each {@link BasicCodeGenerator} from {@link #codeGenerators}:
  * <ol>
  * <li>Instantiate using default constructor</li>
index c6e940115a78c938d41879ca1b74f6a9c6a3f91e..d20964cf33f3d756901f81e534721ab817f8a188 100644 (file)
@@ -7,6 +7,10 @@
  */
 package org.opendaylight.yangtools.yang2sources.plugin;
 
+import java.util.HashSet;
+
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.Maps;
 import java.io.Closeable;
@@ -16,7 +20,6 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -29,7 +32,6 @@ import org.apache.maven.plugin.logging.Log;
 import org.apache.maven.project.MavenProject;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.opendaylight.yangtools.yang.parser.repo.URLSchemaContextResolver;
 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
 import org.opendaylight.yangtools.yang2sources.plugin.ConfigArg.CodeGeneratorArg;
@@ -93,7 +95,8 @@ class YangToSourcesProcessor {
     }
 
     private ContextHolder processYang() throws MojoExecutionException {
-        YangParserImpl parser = new YangParserImpl();
+        final CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR.newBuild();
+        SchemaContext resolveSchemaContext;
         List<Closeable> closeables = new ArrayList<>();
         log.info(Util.message("Inspecting %s", LOG_PREFIX, yangFilesRootDir));
         try {
@@ -140,7 +143,6 @@ class YangToSourcesProcessor {
 
             List<InputStream> all = new ArrayList<>(yangsInProject);
             closeables.addAll(yangsInProject);
-            Map<InputStream, Module> allYangModules;
 
             /**
              * Set contains all modules generated from input sources. Number of
@@ -159,24 +161,21 @@ class YangToSourcesProcessor {
                     closeables.addAll(yangStreams);
                 }
 
-                allYangModules = parser.parseYangModelsFromStreamsMapped(all);
+                resolveSchemaContext = reactor.buildEffective(all);
 
+                Set<Module> parsedAllYangModules = resolveSchemaContext.getModules();
                 projectYangModules = new HashSet<>();
-                for (InputStream inProject : yangsInProject) {
-                    Module module = allYangModules.get(inProject);
-                    if (module != null) {
+                for (Module module : parsedAllYangModules) {
+                    if(module.getModuleSourcePath()!=null) {
                         projectYangModules.add(module);
                     }
                 }
-
             } finally {
                 for (AutoCloseable closeable : closeables) {
                     closeable.close();
                 }
             }
 
-            Set<Module> parsedAllYangModules = new HashSet<>(allYangModules.values());
-            SchemaContext resolveSchemaContext = parser.resolveSchemaContext(parsedAllYangModules);
             log.info(Util.message("%s files parsed from %s", LOG_PREFIX, Util.YANG_SUFFIX.toUpperCase(), yangsInProject));
             return new ContextHolder(resolveSchemaContext, projectYangModules);
 
index d54cf679eadff496a907da1e5dee1489b9e4621a..cf29ed15487492517db90df6f601e99b49a929f0 100644 (file)
@@ -56,7 +56,7 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
 
     @Override
     public void enterStatement(final YangStatementParser.StatementContext ctx) {
-        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getText(), ctx
+        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx
                 .getStart().getLine(), ctx.getStart().getCharPositionInLine());
         boolean action = true;
         QName identifier;
@@ -111,7 +111,7 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
 
     @Override
     public void exitStatement(final YangStatementParser.StatementContext ctx) {
-        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getText(), ctx.getStart().getLine(), ctx
+        final StatementSourceReference ref = DeclarationInTextSource.atPosition(sourceName, ctx.getStart().getLine(), ctx
                 .getStart().getCharPositionInLine());
         for (int i = 0; i < ctx.getChildCount(); i++) {
             ParseTree child = ctx.getChild(i);
index 08d5b9f5faca7c3f9ebad749b36436fa6772f57b..cbe259f1fd9fc1abedb2b0ee7aca82bd6b57a540 100644 (file)
@@ -8,9 +8,17 @@
 package org.opendaylight.yangtools.yang.parser.impl.util;
 
 import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getArgumentString;
-import static org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils.getFirstContext;
+
+import org.opendaylight.yangtools.yang.parser.impl.ParserListenerUtils;
 
 import com.google.common.base.Optional;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import java.util.HashSet;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.StatementContext;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
 import com.google.common.collect.ImmutableSet;
 import java.io.InputStream;
 import java.util.Date;
@@ -25,11 +33,9 @@ import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_date_stmt
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Revision_stmtsContext;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.Submodule_stmtContext;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 
 /**
  * Helper transfer object which holds basic and dependency information for YANG
@@ -58,23 +64,23 @@ public abstract class YangModelDependencyInfo {
     private final ImmutableSet<ModuleImport> dependencies;
 
     YangModelDependencyInfo(final String name, final String formattedRevision,
-            final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+            final ImmutableSet<ModuleImport> imports,
+            final ImmutableSet<ModuleImport> includes) {
         this.name = name;
         this.formattedRevision = formattedRevision;
-        this.revision = formattedRevision == null ? null : QName.parseRevision(formattedRevision);
+        this.revision = formattedRevision == null ? null : QName
+                .parseRevision(formattedRevision);
         this.moduleImports = imports;
         this.submoduleIncludes = includes;
         this.dependencies = ImmutableSet.<ModuleImport> builder()
-                .addAll(moduleImports)
-                .addAll(submoduleIncludes)
-                .build();
+                .addAll(moduleImports).addAll(submoduleIncludes).build();
     }
 
     /**
      * Returns immutable collection of all module imports.
      *
-     * This collection contains both <code>import</code> statements
-     * and <code>include</code> statements for submodules.
+     * This collection contains both <code>import</code> statements and
+     * <code>include</code> statements for submodules.
      *
      * @return Immutable collection of imports.
      */
@@ -148,21 +154,31 @@ public abstract class YangModelDependencyInfo {
     }
 
     /**
-     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree
-     * of a YANG model.
+     * Extracts {@link YangModelDependencyInfo} from an abstract syntax tree of
+     * a YANG model.
      *
-     * @param tree Abstract syntax tree
+     * @param tree
+     *            Abstract syntax tree
      * @return {@link YangModelDependencyInfo}
      * @throws YangSyntaxErrorException
      *             If the AST is not a valid YANG module/submodule
      */
-    public static YangModelDependencyInfo fromAST(final String name, final ParserRuleContext tree) throws YangSyntaxErrorException {
-        final Optional<Module_stmtContext> moduleCtx = getFirstContext(tree, Module_stmtContext.class);
+    public static YangModelDependencyInfo fromAST(final String name,
+            final ParserRuleContext tree) throws YangSyntaxErrorException {
+
+        if (tree instanceof YangStatementParser.StatementContext) {
+            YangStatementParser.StatementContext rootStatement = (YangStatementParser.StatementContext) tree;
+            return parseAST(rootStatement);
+        }
+
+        final Optional<Module_stmtContext> moduleCtx = ParserListenerUtils
+                .getFirstContext(tree, Module_stmtContext.class);
         if (moduleCtx.isPresent()) {
             return parseModuleContext(moduleCtx.get());
         }
 
-        final Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(tree, Submodule_stmtContext.class);
+        final Optional<Submodule_stmtContext> submoduleCtx = ParserListenerUtils
+                .getFirstContext(tree, Submodule_stmtContext.class);
         if (submoduleCtx.isPresent()) {
             return parseSubmoduleContext(submoduleCtx.get());
         }
@@ -170,12 +186,31 @@ public abstract class YangModelDependencyInfo {
         throw new YangSyntaxErrorException(name, 0, 0, "Unknown YANG text type");
     }
 
+    private static YangModelDependencyInfo parseAST(
+            YangStatementParser.StatementContext rootStatement) {
+        if (rootStatement
+                .keyword()
+                .getText()
+                .equals(Rfc6020Mapping.MODULE.getStatementName().getLocalName())) {
+            return parseModuleContext(rootStatement);
+        } else if (rootStatement
+                .keyword()
+                .getText()
+                .equals(Rfc6020Mapping.SUBMODULE.getStatementName()
+                        .getLocalName())) {
+            return parseSubmoduleContext(rootStatement);
+        }
+
+        throw new IllegalArgumentException(
+                "Root of parsed AST must be either module or submodule");
+    }
+
     /**
-     * Extracts {@link YangModelDependencyInfo} from input stream
-     * containing YANG model.
+     * Extracts {@link YangModelDependencyInfo} from input stream containing
+     * YANG model.
      *
-     * This parsing does not validate full YANG module, only
-     * parses header up to the revisions and imports.
+     * This parsing does not validate full YANG module, only parses header up to
+     * the revisions and imports.
      *
      * @param yangStream
      *            Opened Input stream containing text source of YANG model
@@ -183,30 +218,97 @@ public abstract class YangModelDependencyInfo {
      * @throws IllegalArgumentException
      *             If input stream is not valid YANG stream
      */
-    public static YangModelDependencyInfo fromInputStream(final InputStream yangStream) {
-        YangContext yangContext = YangParserImpl.parseStreamWithoutErrorListeners(yangStream);
-
-        Optional<Module_stmtContext> moduleCtx = getFirstContext(yangContext, Module_stmtContext.class);
-        if (moduleCtx.isPresent()) {
-            return parseModuleContext(moduleCtx.get());
-        }
-        Optional<Submodule_stmtContext> submoduleCtx = getFirstContext(yangContext, Submodule_stmtContext.class);
-        if (submoduleCtx.isPresent()) {
-            return parseSubmoduleContext(submoduleCtx.get());
-        }
-        throw new IllegalArgumentException("Supplied stream is not valid yang file.");
+    public static YangModelDependencyInfo fromInputStream(
+            final InputStream yangStream) {
+        StatementContext yangAST = new YangStatementSourceImpl(yangStream)
+                .getYangAST();
+        return parseAST(yangAST);
     }
 
-    private static YangModelDependencyInfo parseModuleContext(final Module_stmtContext module) {
+    private static YangModelDependencyInfo parseModuleContext(
+            final Module_stmtContext module) {
         String name = getArgumentString(module);
         String latestRevision = getLatestRevision(module.revision_stmts());
-        ImmutableSet<ModuleImport> imports = parseImports(module.linkage_stmts().import_stmt());
-        ImmutableSet<ModuleImport> includes = parseIncludes(module.linkage_stmts().include_stmt());
+        ImmutableSet<ModuleImport> imports = parseImports(module
+                .linkage_stmts().import_stmt());
+        ImmutableSet<ModuleImport> includes = parseIncludes(module
+                .linkage_stmts().include_stmt());
+
+        return new ModuleDependencyInfo(name, latestRevision, imports, includes);
+    }
+
+    private static YangModelDependencyInfo parseModuleContext(
+            final YangStatementParser.StatementContext module) {
+        String name = Utils.stringFromStringContext(module.argument());
+        String latestRevision = getLatestRevision(module);
+        ImmutableSet<ModuleImport> imports = parseImports(module);
+        ImmutableSet<ModuleImport> includes = parseIncludes(module);
 
         return new ModuleDependencyInfo(name, latestRevision, imports, includes);
     }
 
-    private static ImmutableSet<ModuleImport> parseImports(final List<Import_stmtContext> importStatements) {
+    private static ImmutableSet<ModuleImport> parseImports(
+            final YangStatementParser.StatementContext module) {
+        Set<ModuleImport> result = new HashSet<>();
+        List<StatementContext> subStatements = module.statement();
+        for (StatementContext subStatementContext : subStatements) {
+            if (subStatementContext
+                    .keyword()
+                    .getText()
+                    .equals(Rfc6020Mapping.IMPORT.getStatementName()
+                            .getLocalName())) {
+                String revisionDateStr = getRevisionDateString(subStatementContext);
+                String importedModuleName = Utils
+                        .stringFromStringContext(subStatementContext.argument());
+                Date revisionDate = (revisionDateStr == null) ? null : QName
+                        .parseRevision(revisionDateStr);
+                result.add(new ModuleImportImpl(importedModuleName,
+                        revisionDate));
+            }
+        }
+        return ImmutableSet.copyOf(result);
+    }
+
+    private static ImmutableSet<ModuleImport> parseIncludes(
+            final YangStatementParser.StatementContext module) {
+        Set<ModuleImport> result = new HashSet<>();
+        List<StatementContext> subStatements = module.statement();
+        for (StatementContext subStatementContext : subStatements) {
+            if (subStatementContext
+                    .keyword()
+                    .getText()
+                    .equals(Rfc6020Mapping.INCLUDE.getStatementName()
+                            .getLocalName())) {
+                String revisionDateStr = getRevisionDateString(subStatementContext);
+                String IncludeModuleName = Utils
+                        .stringFromStringContext(subStatementContext.argument());
+                Date revisionDate = (revisionDateStr == null) ? null : QName
+                        .parseRevision(revisionDateStr);
+                result.add(new ModuleImportImpl(IncludeModuleName, revisionDate));
+            }
+        }
+        return ImmutableSet.copyOf(result);
+    }
+
+    private static String getRevisionDateString(StatementContext importStatement) {
+        List<StatementContext> importSubStatements = importStatement
+                .statement();
+        String revisionDateStr = null;
+        for (StatementContext importSubStatement : importSubStatements) {
+            if (importSubStatement
+                    .keyword()
+                    .getText()
+                    .equals(Rfc6020Mapping.REVISION_DATE.getStatementName()
+                            .getLocalName())) {
+                revisionDateStr = Utils
+                        .stringFromStringContext(importSubStatement.argument());
+            }
+        }
+        return revisionDateStr;
+    }
+
+    private static ImmutableSet<ModuleImport> parseImports(
+            final List<Import_stmtContext> importStatements) {
         ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
         for (Import_stmtContext importStmt : importStatements) {
             String moduleName = getArgumentString(importStmt);
@@ -216,31 +318,89 @@ public abstract class YangModelDependencyInfo {
         return builder.build();
     }
 
-    public static String getLatestRevision(final Revision_stmtsContext revisionStmts) {
-        List<Revision_stmtContext> revisions = revisionStmts.getRuleContexts(Revision_stmtContext.class);
+    public static String getLatestRevision(
+            final YangStatementParser.StatementContext module) {
+        List<StatementContext> subStatements = module.statement();
+        String latestRevision = null;
+        for (StatementContext subStatementContext : subStatements) {
+            if (subStatementContext
+                    .keyword()
+                    .getText()
+                    .equals(Rfc6020Mapping.REVISION.getStatementName()
+                            .getLocalName())) {
+                String currentRevision = Utils
+                        .stringFromStringContext(subStatementContext.argument());
+                if (latestRevision == null
+                        || latestRevision.compareTo(currentRevision) == -1) {
+                    latestRevision = currentRevision;
+                }
+            }
+        }
+        return latestRevision;
+    }
+
+    public static String getLatestRevision(
+            final Revision_stmtsContext revisionStmts) {
+        List<Revision_stmtContext> revisions = revisionStmts
+                .getRuleContexts(Revision_stmtContext.class);
         String latestRevision = null;
         for (Revision_stmtContext revisionStmt : revisions) {
             String currentRevision = getArgumentString(revisionStmt);
-            if (latestRevision == null || latestRevision.compareTo(currentRevision) == -1) {
+            if (latestRevision == null
+                    || latestRevision.compareTo(currentRevision) == -1) {
                 latestRevision = currentRevision;
             }
         }
         return latestRevision;
     }
 
-    private static YangModelDependencyInfo parseSubmoduleContext(final Submodule_stmtContext submodule) {
+    private static YangModelDependencyInfo parseSubmoduleContext(
+            final YangStatementParser.StatementContext submodule) {
+        String name = Utils.stringFromStringContext(submodule.argument());
+        String belongsTo = parseBelongsTo(submodule);
+
+        String latestRevision = getLatestRevision(submodule);
+        ImmutableSet<ModuleImport> imports = parseImports(submodule);
+        ImmutableSet<ModuleImport> includes = parseIncludes(submodule);
+
+        return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
+                imports, includes);
+    }
+
+    private static String parseBelongsTo(StatementContext submodule) {
+        List<StatementContext> subStatements = submodule.statement();
+        for (StatementContext subStatementContext : subStatements) {
+            if (subStatementContext
+                    .keyword()
+                    .getText()
+                    .equals(Rfc6020Mapping.BELONGS_TO.getStatementName()
+                            .getLocalName())) {
+                return Utils.stringFromStringContext(subStatementContext
+                        .argument());
+            }
+        }
+        return null;
+    }
+
+    private static YangModelDependencyInfo parseSubmoduleContext(
+            final Submodule_stmtContext submodule) {
         String name = getArgumentString(submodule);
-        Belongs_to_stmtContext belongsToStmt = submodule.submodule_header_stmts().belongs_to_stmt(0);
+        Belongs_to_stmtContext belongsToStmt = submodule
+                .submodule_header_stmts().belongs_to_stmt(0);
         String belongsTo = getArgumentString(belongsToStmt);
 
         String latestRevision = getLatestRevision(submodule.revision_stmts());
-        ImmutableSet<ModuleImport> imports = parseImports(submodule.linkage_stmts().import_stmt());
-        ImmutableSet<ModuleImport> includes = parseIncludes(submodule.linkage_stmts().include_stmt());
+        ImmutableSet<ModuleImport> imports = parseImports(submodule
+                .linkage_stmts().import_stmt());
+        ImmutableSet<ModuleImport> includes = parseIncludes(submodule
+                .linkage_stmts().include_stmt());
 
-        return new SubmoduleDependencyInfo(name, latestRevision, belongsTo, imports, includes);
+        return new SubmoduleDependencyInfo(name, latestRevision, belongsTo,
+                imports, includes);
     }
 
-    private static ImmutableSet<ModuleImport> parseIncludes(final List<Include_stmtContext> importStatements) {
+    private static ImmutableSet<ModuleImport> parseIncludes(
+            final List<Include_stmtContext> importStatements) {
         ImmutableSet.Builder<ModuleImport> builder = ImmutableSet.builder();
         for (Include_stmtContext importStmt : importStatements) {
             String moduleName = getArgumentString(importStmt);
@@ -250,7 +410,8 @@ public abstract class YangModelDependencyInfo {
         return builder.build();
     }
 
-    private static Date getRevision(final Revision_date_stmtContext revisionDateStmt) {
+    private static Date getRevision(
+            final Revision_date_stmtContext revisionDateStmt) {
         if (revisionDateStmt == null) {
             return null;
         }
@@ -263,32 +424,38 @@ public abstract class YangModelDependencyInfo {
      * Dependency information for YANG module.
      *
      */
-    public static final class ModuleDependencyInfo extends YangModelDependencyInfo {
+    public static final class ModuleDependencyInfo extends
+            YangModelDependencyInfo {
 
-        private ModuleDependencyInfo(final String name, final String latestRevision,
-                final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+        private ModuleDependencyInfo(final String name,
+                final String latestRevision,
+                final ImmutableSet<ModuleImport> imports,
+                final ImmutableSet<ModuleImport> includes) {
             super(name, latestRevision, imports, includes);
         }
 
         @Override
         public String toString() {
-            return "Module [name=" + getName() + ", revision=" + getRevision() + ", dependencies=" + getDependencies()
-                    + "]";
+            return "Module [name=" + getName() + ", revision=" + getRevision()
+                    + ", dependencies=" + getDependencies() + "]";
         }
     }
 
     /**
      *
-     * Dependency information for submodule, also provides name
-     * for parent module.
+     * Dependency information for submodule, also provides name for parent
+     * module.
      *
      */
-    public static final class SubmoduleDependencyInfo extends YangModelDependencyInfo {
+    public static final class SubmoduleDependencyInfo extends
+            YangModelDependencyInfo {
 
         private final String belongsTo;
 
-        private SubmoduleDependencyInfo(final String name, final String latestRevision, final String belongsTo,
-                                        final ImmutableSet<ModuleImport> imports, final ImmutableSet<ModuleImport> includes) {
+        private SubmoduleDependencyInfo(final String name,
+                final String latestRevision, final String belongsTo,
+                final ImmutableSet<ModuleImport> imports,
+                final ImmutableSet<ModuleImport> includes) {
             super(name, latestRevision, imports, includes);
             this.belongsTo = belongsTo;
         }
@@ -303,8 +470,9 @@ public abstract class YangModelDependencyInfo {
 
         @Override
         public String toString() {
-            return "Submodule [name=" + getName() + ", revision=" + getRevision() + ", dependencies="
-                    + getDependencies() + "]";
+            return "Submodule [name=" + getName() + ", revision="
+                    + getRevision() + ", dependencies=" + getDependencies()
+                    + "]";
         }
     }
 
@@ -378,7 +546,8 @@ public abstract class YangModelDependencyInfo {
 
         @Override
         public String toString() {
-            return "ModuleImportImpl [name=" + name + ", revision=" + QName.formattedRevision(revision) + "]";
+            return "ModuleImportImpl [name=" + name + ", revision="
+                    + QName.formattedRevision(revision) + "]";
         }
     }
 }
index 3c2968de577ee1910941017a81588e796cd88d9b..81b99351fd1ddb8235575ec58dc8e519bf51e74a 100644 (file)
@@ -7,8 +7,15 @@
  */
 package org.opendaylight.yangtools.yang.parser.repo;
 
+import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
+
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
 import com.google.common.base.Function;
-import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicate;
 import com.google.common.cache.Cache;
@@ -23,32 +30,21 @@ import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.FutureCallback;
 import com.google.common.util.concurrent.Futures;
 import com.google.common.util.concurrent.ListenableFuture;
-import java.net.URI;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.NavigableMap;
 import java.util.Set;
 import javax.annotation.Nullable;
 import org.antlr.v4.runtime.ParserRuleContext;
-import org.antlr.v4.runtime.tree.ParseTreeWalker;
 import org.opendaylight.yangtools.util.concurrent.ExceptionMapper;
 import org.opendaylight.yangtools.util.concurrent.ReflectiveExceptionMapper;
-import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaContextFactory;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaResolutionException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceFilter;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
-import org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils;
-import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserListenerImpl;
-import org.opendaylight.yangtools.yang.parser.impl.util.YangModelDependencyInfo;
 import org.opendaylight.yangtools.yang.parser.util.ASTSchemaSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -67,7 +63,7 @@ final class SharedSchemaContextFactory implements SchemaContextFactory {
 
     private final AsyncFunction<List<ASTSchemaSource>, SchemaContext> assembleSources = new AsyncFunction<List<ASTSchemaSource>, SchemaContext>() {
         @Override
-        public ListenableFuture<SchemaContext> apply(final List<ASTSchemaSource> sources) throws SchemaResolutionException {
+        public ListenableFuture<SchemaContext> apply(final List<ASTSchemaSource> sources) throws SchemaResolutionException, SourceException, ReactorException {
             final Map<SourceIdentifier, ASTSchemaSource> srcs =
                     Maps.uniqueIndex(sources, ASTSchemaSource.GET_IDENTIFIER);
             final Map<SourceIdentifier, YangModelDependencyInfo> deps =
@@ -78,34 +74,32 @@ final class SharedSchemaContextFactory implements SchemaContextFactory {
             final DependencyResolver res = DependencyResolver.create(deps);
             if (!res.getUnresolvedSources().isEmpty()) {
                 LOG.debug("Omitting models {} due to unsatisfied imports {}", res.getUnresolvedSources(), res.getUnsatisfiedImports());
-
-                // FIXME: push into DependencyResolver
-
                 throw new SchemaResolutionException("Failed to resolve required models",
                         res.getResolvedSources(), res.getUnsatisfiedImports());
             }
 
             final Map<SourceIdentifier, ParserRuleContext> asts =
                     Maps.transformValues(srcs, ASTSchemaSource.GET_AST);
-            final Map<String, NavigableMap<Date, URI>> namespaceContext = BuilderUtils.createYangNamespaceContext(
-                    asts.values(), Optional.<SchemaContext>absent());
 
-            final ParseTreeWalker walker = new ParseTreeWalker();
-            final Map<SourceIdentifier, ModuleBuilder> sourceToBuilder = new LinkedHashMap<>();
-
-            for (final Entry<SourceIdentifier, ParserRuleContext> entry : asts.entrySet()) {
-                final ModuleBuilder moduleBuilder = YangParserListenerImpl.create(namespaceContext, entry.getKey().getName(),
-                        walker, entry.getValue()).getModuleBuilder();
-
-                moduleBuilder.setSource(srcs.get(entry.getKey()).getYangText());
-                sourceToBuilder.put(entry.getKey(), moduleBuilder);
+            CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+                    .newBuild();
+
+            for (final Entry<SourceIdentifier, ParserRuleContext> entry : asts
+                    .entrySet()) {
+                ParserRuleContext parserRuleCtx = entry.getValue();
+                if (parserRuleCtx instanceof YangStatementParser.StatementContext) {
+                    YangStatementSourceImpl source = new YangStatementSourceImpl(
+                            entry.getKey(),
+                            (YangStatementParser.StatementContext) parserRuleCtx);
+                    reactor.addSource(source);
+                } else {
+                    throw new IllegalArgumentException("Only YangStatementParser.StatementContext is supported.");
+                }
             }
-            LOG.debug("Modules ready for integration");
 
-            final YangParserImpl parser = YangParserImpl.getInstance();
-            final Collection<Module> modules = parser.buildModules(sourceToBuilder.values());
-            LOG.debug("Integrated cross-references modules");
-            return Futures.immediateCheckedFuture(parser.assembleContext(modules));
+            SchemaContext schemaContext = reactor.buildEffective();
+
+            return Futures.immediateCheckedFuture(schemaContext);
         }
     };
 
index 6ff73908b9fbc1156ed37f6d63c851bcd2c15b93..fb05b40202491eaeb4b05b0f7cf82e9f07a22e4b 100644 (file)
@@ -20,12 +20,12 @@ import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
  *
  *  To create source reference use one of this static factories:
  *  <ul>
- *  <li>{@link #atPosition(String, String, int, int)} - provides most specific reference of statement location,
+ *  <li>{@link #atPosition(String, int, int)} - provides most specific reference of statement location,
  *  this is most prefered since it provides most context to debug YANG model.
  *  </li>
- *  <li>{@link #atLine(String, String, int)}- provides source and line of statement location.
+ *  <li>{@link #atLine(String, int)}- provides source and line of statement location.
  *  </li>
- *  <li>{@link #inSource(String, String)} - least specific reference, should be used only if any of previous
+ *  <li>{@link #inSource(String)} - least specific reference, should be used only if any of previous
  *  references are unable to create / derive from source.
  *  </li>
  *  </ul>
@@ -33,22 +33,16 @@ import org.opendaylight.yangtools.yang.model.api.meta.StatementSource;
  */
 public abstract class DeclarationInTextSource implements StatementSourceReference {
 
-    private final String sourceText;
     private final String sourceName;
 
-    DeclarationInTextSource(String sourceName, String sourceText) {
+    DeclarationInTextSource(String sourceName) {
         this.sourceName = sourceName;
-        this.sourceText = sourceText;
     }
 
     public String getSourceName() {
         return sourceName;
     }
 
-    public String getSourceText() {
-        return sourceText;
-    }
-
     @Override
     public StatementSource getStatementSource() {
         return StatementSource.DECLARATION;
@@ -57,22 +51,22 @@ public abstract class DeclarationInTextSource implements StatementSourceReferenc
     @Override
     public abstract String toString();
 
-    public static final DeclarationInTextSource inSource(String sourceName, String sourceText) {
-        return new InSource(sourceName, sourceText);
+    public static final DeclarationInTextSource inSource(String sourceName) {
+        return new InSource(sourceName);
     }
 
-    public static final DeclarationInTextSource atLine(String sourceName, String sourceText, int line) {
-        return new AtLine(sourceName, sourceText, line);
+    public static final DeclarationInTextSource atLine(String sourceName, int line) {
+        return new AtLine(sourceName, line);
     }
 
-    public static final DeclarationInTextSource atPosition(String sourceName, String sourceText, int line, int position) {
-        return new AtPosition(sourceName, sourceText, line,position);
+    public static final DeclarationInTextSource atPosition(String sourceName, int line, int position) {
+        return new AtPosition(sourceName, line,position);
     }
 
     private static class InSource extends DeclarationInTextSource {
 
-        public InSource(String sourceName, String sourceText) {
-            super(sourceName, sourceText);
+        InSource(String sourceName) {
+            super(sourceName);
         }
 
         @Override
@@ -82,12 +76,12 @@ public abstract class DeclarationInTextSource implements StatementSourceReferenc
 
     }
 
-    private static class AtLine extends DeclarationInTextSource {
+    private static class AtLine extends InSource {
 
         private final int line;
 
-        public AtLine(String sourceName, String sourceText, int line) {
-            super(sourceName, sourceText);
+        AtLine(String sourceName, int line) {
+            super(sourceName);
             this.line = line;
         }
 
@@ -95,23 +89,25 @@ public abstract class DeclarationInTextSource implements StatementSourceReferenc
         public String toString() {
             return String.format("%s:%d", getSourceName(),line);
         }
+        
+        public int getLine() {
+            return line;
+        }
 
     }
 
-    private static class AtPosition extends DeclarationInTextSource {
+    private static class AtPosition extends AtLine {
 
-        private int line;
-        private int character;
+        private final int character;
 
-        public AtPosition(String sourceName, String sourceText, int line, int character) {
-            super(sourceName, sourceText);
-            this.line = line;
+        AtPosition(String sourceName, int line, int character) {
+            super(sourceName, line);
             this.character = character;
         }
 
         @Override
         public String toString() {
-            return String.format("%s:%d:%d", getSourceName(),line,character);
+            return String.format("%s:%d:%d", getSourceName(),getLine(),character);
         }
 
     }
index 3b8b11007f4d5320106f99646aca88889387c151..c206e0e6375c80b65f6f67062eff1ca23eed1f03 100644 (file)
@@ -7,8 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
-import java.util.Set;
+import java.io.IOException;
 
+import com.google.common.io.ByteSource;
+import java.util.Set;
 import java.io.FileNotFoundException;
 import org.opendaylight.yangtools.yang.parser.util.NamedFileInputStream;
 import java.util.HashMap;
@@ -105,6 +107,15 @@ public class CrossSourceStatementReactor {
             return context.buildEffective();
         }
 
+        public SchemaContext buildEffective(Collection<ByteSource> yangByteSources) throws SourceException, ReactorException, IOException {
+
+            for(ByteSource yangByteSource : yangByteSources) {
+                addSource(new YangStatementSourceImpl(yangByteSource.openStream()));
+            }
+
+            return buildEffective();
+        }
+
         public SchemaContext buildEffective(List<InputStream> yangInputStreams) throws SourceException, ReactorException {
 
             for(InputStream yangInputStream : yangInputStreams) {
index 3de0b1eafaff9c621d82d4d122da1388f8dba6fe..5acbf5959a2260a1fc37edc3d8f2202c5fc1d565 100644 (file)
@@ -31,7 +31,6 @@ import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
 
 public class EffectiveSchemaContext extends AbstractEffectiveSchemaContext {
 
-    private final Map<ModuleIdentifier, String> identifiersToSources;
     private final SetMultimap<URI, Module> namespaceToModules;
     private final SetMultimap<String, Module> nameToModules;
     private final Set<Module> modules;
@@ -70,13 +69,10 @@ public class EffectiveSchemaContext extends AbstractEffectiveSchemaContext {
 
         namespaceToModules = ImmutableSetMultimap.copyOf(nsMap);
         nameToModules = ImmutableSetMultimap.copyOf(nameMap);
-        identifiersToSources = ImmutableMap.copyOf(isMap);
-
     }
 
-    public EffectiveSchemaContext(final Set<Module> modules, final Map<ModuleIdentifier, String> identifiersToSources) {
-        this.identifiersToSources = ImmutableMap.copyOf(identifiersToSources);
-
+    public EffectiveSchemaContext(final Set<Module> modules) {
+        
          /*
          * Instead of doing this on each invocation of getModules(), pre-compute
          * it once and keep it around -- better than the set we got in.
@@ -109,11 +105,7 @@ public class EffectiveSchemaContext extends AbstractEffectiveSchemaContext {
     }
 
     public static SchemaContext resolveSchemaContext(final Set<Module> modules) {
-        Map<ModuleIdentifier, String> identifiersToSources = new HashMap<>();
-        for (Module module : modules) {
-            identifiersToSources.put(module, module.getSource());
-        }
-        return new EffectiveSchemaContext(modules, identifiersToSources);
+       return new EffectiveSchemaContext(modules);
     }
 
     public ImmutableList<DeclaredStatement<?>> getRootDeclaredStatements() {
@@ -126,7 +118,7 @@ public class EffectiveSchemaContext extends AbstractEffectiveSchemaContext {
 
     @Override
     protected Map<ModuleIdentifier, String> getIdentifiersToSources() {
-        return identifiersToSources;
+        return ImmutableMap.of();
     }
 
     @Override
index d92f2ba213549f4b190098ee2391d8dd96043a50..5e673783f024828c2d8c33ca8c78de056feccad2 100644 (file)
@@ -73,7 +73,6 @@ public class ModuleEffectiveStatementImpl extends AbstractEffectiveDocumentedNod
     private ImmutableList<ExtensionDefinition> extensionNodes;
     private ImmutableSet<IdentitySchemaNode> identities;
     private ImmutableList<UnknownSchemaNode> unknownNodes;
-    private final String source;
     private ImmutableList<EffectiveStatement<?, ?>> substatementsOfSubmodules;
 
     private ImmutableMap<QName, DataSchemaNode> childNodes;
@@ -110,10 +109,12 @@ public class ModuleEffectiveStatementImpl extends AbstractEffectiveDocumentedNod
         if (yangVersion == null) {
             yangVersion = "1";
         }
+        if(ctx.getStatementSourceReference() instanceof DeclarationInTextSource) {
+            sourcePath = ((DeclarationInTextSource) ctx.getStatementSourceReference()).getSourceName();
+        } else {
+            sourcePath = null;
+        }
 
-        DeclarationInTextSource sourceReference = (DeclarationInTextSource) ctx.getStatementSourceReference();
-        sourcePath = sourceReference.getSourceName();
-        source = sourceReference.getSourceText();
 
         initSubmodules(ctx);
         initSubstatementCollections(ctx);
@@ -406,7 +407,7 @@ public class ModuleEffectiveStatementImpl extends AbstractEffectiveDocumentedNod
 
     @Override
     public String getSource() {
-        return source;
+        return null;
     }
 
     @Override
index 560ac833735e833104d6ebca5842878cd1d32929..aa1b73e5c915af44d91aec21dee72b91873f00bd 100644 (file)
@@ -8,22 +8,20 @@
 
 package org.opendaylight.yangtools.yang.parser.util;
 
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
 import com.google.common.annotations.Beta;
 import com.google.common.base.Charsets;
 import com.google.common.util.concurrent.CheckedFuture;
 import com.google.common.util.concurrent.Futures;
 import java.io.IOException;
 import java.io.InputStream;
-import org.antlr.v4.runtime.tree.ParseTreeWalker;
-import org.opendaylight.yangtools.antlrv4.code.gen.YangParser.YangContext;
 import org.opendaylight.yangtools.yang.model.parser.api.YangSyntaxErrorException;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaRepository;
 import org.opendaylight.yangtools.yang.model.repo.api.SchemaSourceException;
 import org.opendaylight.yangtools.yang.model.repo.api.YangTextSchemaSource;
 import org.opendaylight.yangtools.yang.model.repo.spi.SchemaSourceRegistry;
 import org.opendaylight.yangtools.yang.model.repo.util.SchemaSourceTransformer;
-import org.opendaylight.yangtools.yang.parser.impl.YangModelBasicValidationListener;
-import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -33,17 +31,15 @@ import org.slf4j.LoggerFactory;
  */
 @Beta
 public final class TextToASTTransformer extends SchemaSourceTransformer<YangTextSchemaSource, ASTSchemaSource> {
+
     public static final class TextToASTTransformation implements Transformation<YangTextSchemaSource, ASTSchemaSource> {
         @Override
         public CheckedFuture<ASTSchemaSource, SchemaSourceException> apply(final YangTextSchemaSource input) throws IOException, YangSyntaxErrorException {
             try (InputStream is = input.openStream()) {
-                final YangContext ctx = YangParserImpl.parseYangSource(is);
+                final ParserRuleContext ctx = new YangStatementSourceImpl(is).getYangAST();
                 LOG.debug("Model {} parsed successfully", input);
 
-                final ParseTreeWalker walker = new ParseTreeWalker();
-                final YangModelBasicValidationListener validator = new YangModelBasicValidationListener();
-                walker.walk(validator, ctx);
-                LOG.debug("Model {} validated successfully", input);
+                //:TODO missing validation (YangModelBasicValidationListener should be re-implemented to new parser)
 
                 // Backwards compatibility
                 final String text = input.asCharSource(Charsets.UTF_8).read();
diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ModuleSourceTest.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/ModuleSourceTest.java
deleted file mode 100644 (file)
index e214b2a..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.opendaylight.yangtools.yang.stmt.test;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.URISyntaxException;
-import java.util.Set;
-import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
-import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
-
-public class ModuleSourceTest {
-
-    @Test
-    public void test() throws SourceException, ReactorException, URISyntaxException, IOException {
-        SchemaContext schema = StmtTestUtils.parseYangSources("/module-source");
-
-        assertNotNull(schema);
-
-        Set<Module> modules = schema.getModules();
-        assertNotNull(modules);
-        assertEquals(1,modules.size());
-
-        Module simpleModule = modules.iterator().next();
-        String source = simpleModule.getSource();
-        String moduleSourcePath = simpleModule.getModuleSourcePath();
-
-        File simpleYang = new File(getClass().getResource("/module-source/simple-module.yang").toURI());
-
-        assertEquals(simpleYang.getPath(), moduleSourcePath);
-        assertEquals(readFile(moduleSourcePath), source);
-    }
-
-    private static String readFile(final String fileName) throws IOException {
-        BufferedReader br = new BufferedReader(new FileReader(fileName));
-        try {
-            StringBuilder sb = new StringBuilder();
-            String line = br.readLine();
-
-            while (line != null) {
-                sb.append(line);
-                sb.append(System.lineSeparator());
-                line = br.readLine();
-            }
-            return sb.toString();
-        } finally {
-            br.close();
-        }
-    }
-}