Merge "add new tests BindingTypesTest, BaseYangTypesTest"
authorLukas Sedlak <lsedlak@cisco.com>
Thu, 21 Aug 2014 09:40:51 +0000 (09:40 +0000)
committerGerrit Code Review <gerrit@opendaylight.org>
Thu, 21 Aug 2014 09:40:51 +0000 (09:40 +0000)
13 files changed:
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataContainerCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java
code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend
code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend
common/features/pom.xml
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java
yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java
yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java

index dcaecd34c8b637c19f4f1a92dad692aa5eb46c8a..700476f86bae3d79c3ed8c8d4d6e4315a7bac807 100644 (file)
@@ -11,10 +11,8 @@ import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Iterables;
-
 import java.util.HashMap;
 import java.util.Map;
-
 import org.opendaylight.yangtools.yang.binding.DataObject;
 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
@@ -59,7 +57,18 @@ final class ChoiceNodeCodecContext extends DataContainerCodecContext<ChoiceNode>
 
     @Override
     protected DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) {
-        return byClass.get(childClass).get();
+        DataContainerCodecPrototype<?> child = byClass.get(childClass);
+        Preconditions.checkArgument(child != null,"Supplied class is not valid case",childClass);
+        return child.get();
+    }
+
+    @Override
+    protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
+        DataContainerCodecPrototype<?> child = byClass.get(childClass);
+        if(child != null) {
+            return Optional.<DataContainerCodecContext<?>>of(child.get());
+        }
+        return Optional.absent();
     }
 
     Iterable<Class<?>> getCaseChildrenClasses() {
index 6b1124c30e96e012b14c4b0ab6ebe9fec83a8698..e873126598ba7f8cac91c29a1cbf7983c39e56ed 100644 (file)
@@ -7,8 +7,8 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
+import com.google.common.base.Optional;
 import java.util.List;
-
 import org.opendaylight.yangtools.yang.binding.BindingStreamEventWriter;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
@@ -92,8 +92,20 @@ abstract class DataContainerCodecContext<T> extends NodeCodecContext {
      *
      * @param childClass
      * @return Context of child
+     * @throws IllegalArgumentException If supplied child class is not valid in specified context.
      */
-    protected abstract DataContainerCodecContext<?> getStreamChild(final Class<?> childClass);
+    protected abstract DataContainerCodecContext<?> getStreamChild(final Class<?> childClass) throws IllegalArgumentException;
+
+    /**
+    *
+    * Returns child context as if it was walked by
+    * {@link BindingStreamEventWriter}. This means that to enter case, one
+    * must issue getChild(ChoiceClass).getChild(CaseClass).
+    *
+    * @param childClass
+    * @return Context of child or Optional absent is supplied class is not applicable in context.
+    */
+   protected abstract Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass);
 
     @Override
     public String toString() {
index 321f8ded5b5acaa573fc3cd1c5cf0612692efca4..32fdf6d80ea597da1c4ec000bcecb880c9564564 100644 (file)
@@ -10,14 +10,12 @@ package org.opendaylight.yangtools.binding.data.codec.impl;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
-
 import java.lang.reflect.Method;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
-
 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
 import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
@@ -113,6 +111,15 @@ abstract class DataObjectCodecContext<T extends DataNodeContainer> extends DataC
         return childProto.get();
     }
 
+    @Override
+    protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
+        DataContainerCodecPrototype<?> childProto = byStreamClass.get(childClass);
+        if(childProto != null) {
+            return Optional.<DataContainerCodecContext<?>>of(childProto.get());
+        }
+        return Optional.absent();
+    }
+
     @Override
     protected DataContainerCodecContext<?> getIdentifierChild(final InstanceIdentifier.PathArgument arg,
             final List<YangInstanceIdentifier.PathArgument> builder) {
index d7ede0fbb83ad1dc719ab4e211b0095ca8c70959..c86585c7e09c6541069fed00158c306dc625f753 100644 (file)
@@ -11,7 +11,6 @@ import com.google.common.base.Objects.ToStringHelper;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.collect.ImmutableMap;
-
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -19,7 +18,6 @@ import java.lang.reflect.Proxy;
 import java.util.Map;
 import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
-
 import org.opendaylight.yangtools.binding.data.codec.util.AugmentationReader;
 import org.opendaylight.yangtools.yang.binding.Augmentable;
 import org.opendaylight.yangtools.yang.binding.Augmentation;
@@ -164,11 +162,13 @@ class LazyDataObject implements InvocationHandler, AugmentationReader {
         if (aug != null) {
             return aug.get(cls);
         }
-
-        final DataContainerCodecContext<?> augCtx = context.getStreamChild(cls);
-        final Optional<NormalizedNode<?, ?>> augData = data.getChild(augCtx.getDomPathArgument());
-        if (augData.isPresent()) {
-            return augCtx.dataFromNormalizedNode(augData.get());
+        Preconditions.checkNotNull(cls,"Supplied augmentation must not be null.");
+        final Optional<DataContainerCodecContext<?>> augCtx= context.getPossibleStreamChild(cls);
+        if(augCtx.isPresent()) {
+            final Optional<NormalizedNode<?, ?>> augData = data.getChild(augCtx.get().getDomPathArgument());
+            if (augData.isPresent()) {
+                return augCtx.get().dataFromNormalizedNode(augData.get());
+            }
         }
         return null;
     }
index 1a57b21623f7f7dec3c27e66275e873d5792c90f..be328ca199ddd388df298159df847e3d90b6f46d 100644 (file)
@@ -7,11 +7,11 @@
  */
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
+import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
-
 import org.opendaylight.yangtools.util.ClassLoaderUtils;
 import org.opendaylight.yangtools.yang.binding.ChildOf;
 import org.opendaylight.yangtools.yang.binding.DataRoot;
@@ -59,6 +59,11 @@ class SchemaRootCodecContext extends DataContainerCodecContext<SchemaContext> {
         return children.getUnchecked(childClass);
     }
 
+    @Override
+    protected Optional<DataContainerCodecContext<?>> getPossibleStreamChild(final Class<?> childClass) {
+        throw new UnsupportedOperationException("Not supported");
+    }
+
     @Override
     protected YangInstanceIdentifier.PathArgument getDomPathArgument() {
         throw new UnsupportedOperationException();
index d657087326b61d962b025fb52fff82e1f0d883ca..9c66f8d6a6bcb91f04d25ffb95fe59d5dc17428a 100644 (file)
@@ -1345,7 +1345,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                 } else if (typeDef instanceof BitsTypeDefinition) {
                     genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, leaf, parentModule);
                     if (genTOBuilder != null) {
-                        returnType = new ReferencedTypeImpl(genTOBuilder.getPackageName(), genTOBuilder.getName());
+                        returnType = genTOBuilder.toInstance();
                     }
                 } else {
                     final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
@@ -1436,13 +1436,8 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     // GeneratedType for this type definition should be already
                     // created
                     QName qname = typeDef.getQName();
-                    Module unionModule = null;
-                    String prefix = qname.getPrefix();
-                    if (prefix == null || prefix.isEmpty() || prefix.equals(module.getPrefix())) {
-                        unionModule = module;
-                    } else {
-                        unionModule = findModuleFromImports(module.getImports(), qname.getPrefix());
-                    }
+                    Module unionModule = schemaContext.findModuleByNamespaceAndRevision(qname.getNamespace(),
+                            qname.getRevision());
                     final ModuleContext mc = genCtx.get(unionModule);
                     returnType = mc.getTypedefs().get(typeDef.getPath());
                 } else {
@@ -1532,7 +1527,7 @@ public class BindingGeneratorImpl implements BindingGenerator {
                     }
                 } else if (typeDef instanceof BitsTypeDefinition) {
                     final GeneratedTOBuilder genTOBuilder = addTOToTypeBuilder(typeDef, typeBuilder, node, parentModule);
-                    returnType = new ReferencedTypeImpl(genTOBuilder.getPackageName(), genTOBuilder.getName());
+                    returnType = genTOBuilder.toInstance();
                 } else {
                     final Restrictions restrictions = BindingGeneratorUtil.getRestrictions(typeDef);
                     returnType = typeProvider.javaTypeForSchemaDefinitionType(typeDef, node, restrictions);
index 396001dbd31d95fd343f73cb47e0367f37b5cc5c..98d7ef1d9e120bc4540bf7fa7e8d910ac58cf125 100644 (file)
@@ -410,7 +410,7 @@ class YangTemplate {
         '''
             identity «identity.QName.localName» {
                 «IF identity.baseIdentity != null»
-                base "«writeIdentityPrefix(identity.baseIdentity)»«identity.baseIdentity»";
+                base "(«writeIdentityNs(identity.baseIdentity)»)«identity.baseIdentity»";
                 «ENDIF»
                 «IF !identity.description.nullOrEmpty»
                 description
@@ -427,17 +427,14 @@ class YangTemplate {
         '''
     }
 
-    def static writeIdentityPrefix(IdentitySchemaNode identity) {
+    def static writeIdentityNs(IdentitySchemaNode identity) {
         if(module == null)
             return ''
 
-        if(identity.QName.prefix.nullOrEmpty || module.prefix.nullOrEmpty)
-            return ''
-
-        val identityPrefix = identity.QName.prefix
+        val identityNs = identity.QName.namespace
 
-        if(!module.prefix.equals(identity.QName.prefix))
-            return identityPrefix + ":"
+        if(!module.namespace.equals(identityNs))
+            return identityNs + ":"
         return ''
     }
 
@@ -785,12 +782,13 @@ class YangTemplate {
         val StringBuilder sb = new StringBuilder();
 
         for(pathElement : schemaPath) {
-            val prefix = pathElement.prefix
+            val ns = pathElement.namespace
             val localName = pathElement.localName
 
             sb.append("\\")
-            sb.append(prefix)
-            sb.append(":")
+            sb.append('(')
+            sb.append(ns)
+            sb.append(')')
             sb.append(localName)
         }
         return sb.toString
index 4a78be34a8971abc9aa59839288777dae16e0346..d8c49592965f0cb1f62654aed4bff87894c0269e 100644 (file)
@@ -54,6 +54,7 @@ import org.sonatype.plexus.build.incremental.BuildContext
 import org.sonatype.plexus.build.incremental.DefaultBuildContext
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier
+import com.google.common.collect.Iterables
 
 class GeneratorImpl {
 
@@ -339,11 +340,8 @@ class GeneratorImpl {
     private def parseTargetPath(SchemaPath path) {
         val List<DataSchemaNode> nodes = new ArrayList<DataSchemaNode>();
         for (QName pathElement : path.pathFromRoot) {
-            val prefix = pathElement.prefix
-            val moduleName = imports.get(prefix)
-            if(moduleName != null) {
-                val revision = pathElement.revision
-                val module = ctx.findModuleByName(moduleName, revision)
+            val module = ctx.findModuleByNamespaceAndRevision(pathElement.namespace, pathElement.revision);
+            if (module !== null) {
                 var foundNode = module.getDataChildByName(pathElement)
                 if(foundNode == null) {
                     val child = nodes.last
@@ -833,18 +831,15 @@ class GeneratorImpl {
 
     def String typeAnchorLink(SchemaPath path, CharSequence text) {
         if(path !== null) {
-            val prefix = path.path.last.prefix
-            if(prefix == this.currentModule.prefix) {
+            val lastElement = Iterables.getLast(path.pathFromRoot)
+            val ns = lastElement.namespace
+            if (ns == this.currentModule.namespace) {
                 return '''<a href="#«path.schemaPathToId»">«text»</a>'''
-            } else if(!prefix.nullOrEmpty){
-                val String module = imports.get(prefix)
-                if(!module.nullOrEmpty) {
-                    return '''«prefix»:«text»'''
-                    //to enable external (import) links
-                    //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
-                }
+            } else {
+                return '''(«ns»)«text»'''
+                //to enable external (import) links
+                //return '''<a href="«module».html#«path.schemaPathToId»">«prefix»:«text»</a>'''
             }
-            return text.toString
         }
     }
 
@@ -1233,13 +1228,8 @@ class GeneratorImpl {
                     }
                 }
 
-                var String prefix = name.prefix
-                var String moduleName
-                if (prefix == null || prefix.empty || prefix.equals(module.prefix)) {
-                    moduleName = module.name
-                } else {
-                    moduleName = imports.get(prefix)
-                }
+                val pathElementModule = ctx.findModuleByNamespaceAndRevision(name.namespace, name.revision)
+                val String moduleName = pathElementModule.name
                 pathString.append(moduleName)
                 pathString.append(':')
                 pathString.append(name.localName)
index 8ce171760d818c55898b2492338a0c2702d95247..4fe4b110c25aee5520cad789b7cfb49dd2634009 100644 (file)
      <dependency>
        <groupId>org.opendaylight.yangtools.thirdparty</groupId>
        <artifactId>xtend-lib-osgi</artifactId>
+       <scope>compile</scope>
      </dependency>
      <!-- test the features.xml -->
      <dependency>
index 6a7d67e3ad15e41b668808663cd12b10d50a0a0c..6667076fb40d0f647658ffd302d95d19426136d7 100644 (file)
@@ -7,15 +7,10 @@
  */
 package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
-import static com.google.common.base.Preconditions.checkArgument;
-
-import java.util.Map;
-
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode;
 import org.opendaylight.yangtools.yang.data.api.schema.MapNode;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
@@ -42,9 +37,9 @@ import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Iterables;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
 
 abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareApplyOperation {
 
@@ -64,14 +59,6 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
     }
 
-    @Override
-    protected void checkWriteApplicable(final YangInstanceIdentifier path, final NodeModification modification,
-            final Optional<TreeNode> current) throws DataValidationFailedException {
-        // FIXME: Implement proper write check for replacement of node container
-        //        prerequisite is to have transaction chain available for clients
-        //        otherwise this will break chained writes to same node.
-    }
-
     @SuppressWarnings("rawtypes")
     @Override
     protected void verifyWrittenStructure(final NormalizedNode<?, ?> writtenValue) {
@@ -126,7 +113,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
             final Version nodeVersion, final Iterable<ModifiedNode> modifications) {
 
         for (ModifiedNode mod : modifications) {
-            final PathArgument id = mod.getIdentifier();
+            final YangInstanceIdentifier.PathArgument id = mod.getIdentifier();
             final Optional<TreeNode> cm = meta.getChild(id);
 
             Optional<TreeNode> result = resolveChildOperation(id).apply(mod, cm, nodeVersion);
@@ -177,7 +164,7 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
     private void checkChildPreconditions(final YangInstanceIdentifier path, final NodeModification modification, final Optional<TreeNode> current) throws DataValidationFailedException {
         final TreeNode currentMeta = current.get();
         for (NodeModification childMod : modification.getChildren()) {
-            final PathArgument childId = childMod.getIdentifier();
+            final YangInstanceIdentifier.PathArgument childId = childMod.getIdentifier();
             final Optional<TreeNode> childMeta = currentMeta.getChild(childId);
 
             YangInstanceIdentifier childPath = path.node(childId);
@@ -198,23 +185,23 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
 
     public static class ChoiceModificationStrategy extends NormalizedNodeContainerModificationStrategy {
 
-        private final Map<PathArgument, ModificationApplyOperation> childNodes;
+        private final Map<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> childNodes;
 
         public ChoiceModificationStrategy(final ChoiceNode schemaNode) {
             super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class);
-            ImmutableMap.Builder<PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
+            ImmutableMap.Builder<YangInstanceIdentifier.PathArgument, ModificationApplyOperation> child = ImmutableMap.builder();
 
             for (ChoiceCaseNode caze : schemaNode.getCases()) {
                 for (DataSchemaNode cazeChild : caze.getChildNodes()) {
                     SchemaAwareApplyOperation childNode = SchemaAwareApplyOperation.from(cazeChild);
-                    child.put(new NodeIdentifier(cazeChild.getQName()), childNode);
+                    child.put(new YangInstanceIdentifier.NodeIdentifier(cazeChild.getQName()), childNode);
                 }
             }
             childNodes = child.build();
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument child) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument child) {
             return Optional.fromNullable(childNodes.get(child));
         }
 
@@ -244,8 +231,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeWithValue) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeWithValue) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -269,8 +256,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeIdentifierWithPredicates) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -300,8 +287,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeWithValue) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeWithValue) {
                 return entryStrategy;
             }
             return Optional.absent();
@@ -325,8 +312,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp
         }
 
         @Override
-        public Optional<ModificationApplyOperation> getChild(final PathArgument identifier) {
-            if (identifier instanceof NodeIdentifierWithPredicates) {
+        public Optional<ModificationApplyOperation> getChild(final YangInstanceIdentifier.PathArgument identifier) {
+            if (identifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) {
                 return entryStrategy;
             }
             return Optional.absent();
index 915f2fbf878333f5a8e720b90374c4f78761caf0..d0a0bcd33fe9d7d82df72dedd2700be4fc3f62e7 100644 (file)
@@ -9,9 +9,6 @@ package org.opendaylight.yangtools.yang.data.impl.schema.tree;
 
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
-
-import java.util.List;
-
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
@@ -37,6 +34,8 @@ import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.List;
+
 abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
     private static final Logger LOG = LoggerFactory.getLogger(SchemaAwareApplyOperation.class);
 
@@ -165,6 +164,8 @@ abstract class SchemaAwareApplyOperation implements ModificationApplyOperation {
             checkNotConflicting(path, original.get(), current.get());
         } else if(original.isPresent()) {
             throw new ConflictingModificationAppliedException(path,"Node was deleted by other transaction.");
+        } else if(current.isPresent()) {
+            throw new ConflictingModificationAppliedException(path,"Node was created by other transaction.");
         }
     }
 
diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java
new file mode 100644 (file)
index 0000000..7525dc3
--- /dev/null
@@ -0,0 +1,650 @@
+package org.opendaylight.yangtools.yang.data.impl.schema.tree;
+
+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.impl.schema.ImmutableNodes;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.impl.ImmutableContainerNodeBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+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;
+
+public class ConcurrentTreeModificationTest {
+    private static final Logger LOG = LoggerFactory.getLogger(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;
+
+    @Before
+    public void prepare() {
+        schemaContext = TestModel.createTestContext();
+        assertNotNull("Schema context must not be null.", schemaContext);
+        rootOper = RootModificationApplyOperation.from(SchemaAwareApplyOperation.from(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();
+    }
+
+    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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        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));
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeMerge1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        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));
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        Optional<NormalizedNode<?, ?>> testNodeAfterCommits = modificationTree1.readNode(TestModel.TEST_PATH);
+        assertPresentAndType(testNodeAfterCommits, ContainerNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar1stLevelEmptyTreeTest() throws DataValidationFailedException {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(TestModel.TEST_PATH, createFooTestContainerNode());
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.write(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            fail("Exception should have been thrown.");
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+        } catch (ConflictingModificationAppliedException ex) {
+            LOG.debug("ConflictingModificationAppliedException - '{}' was thrown as expected.");
+        }
+
+
+        InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar1stLevelEmptyContainerTest() throws DataValidationFailedException {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        DataTreeModification initialDataTreeModification = inMemoryDataTree.takeSnapshot().newModification();
+        initialDataTreeModification.write(TestModel.TEST_PATH, ImmutableNodes.containerNode(TestModel.TEST_QNAME));
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(TestModel.TEST_PATH, createBarTestContainerNode());
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertPresentAndType(snapshotAfterCommits.readNode(OUTER_LIST_2_PATH), MapEntryNode.class);
+    }
+
+    @Test
+    public void writeWriteFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.write(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.write(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.merge(OUTER_LIST_1_PATH, FOO_NODE);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        inMemoryDataTree.validate(modificationTree2);
+        DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+        inMemoryDataTree.commit(prepare2);
+
+        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 {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (Exception e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree");
+        }
+
+        InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+
+    @Test
+    public void deleteMergeFooBar2ndLevelEmptyContainerTest() throws DataValidationFailedException {
+        InMemoryDataTree inMemoryDataTree = InMemoryDataTreeFactory.getInstance().create();
+        inMemoryDataTree.setSchemaContext(schemaContext);
+        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());
+        inMemoryDataTree.commit(inMemoryDataTree.prepare(initialDataTreeModification));
+        InMemoryDataTreeSnapshot initialDataTreeSnapshot = inMemoryDataTree.takeSnapshot();
+
+        DataTreeModification modificationTree1 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+        DataTreeModification modificationTree2 = new InMemoryDataTreeModification(initialDataTreeSnapshot,
+                rootOper);
+
+        modificationTree1.delete(TestModel.TEST_PATH);
+        modificationTree2.merge(OUTER_LIST_2_PATH, BAR_NODE);
+
+        inMemoryDataTree.validate(modificationTree1);
+        DataTreeCandidate prepare1 = inMemoryDataTree.prepare(modificationTree1);
+        inMemoryDataTree.commit(prepare1);
+
+        try {
+            inMemoryDataTree.validate(modificationTree2);
+            DataTreeCandidate prepare2 = inMemoryDataTree.prepare(modificationTree2);
+            inMemoryDataTree.commit(prepare2);
+            fail("Exception should have been thrown");
+        } catch (Exception e) {
+            LOG.debug("Exception was thrown because path no longer exist in tree");
+        }
+
+        InMemoryDataTreeSnapshot snapshotAfterCommits = inMemoryDataTree.takeSnapshot();
+        assertFalse(snapshotAfterCommits.readNode(TestModel.TEST_PATH).isPresent());
+    }
+}
index 5b5fbbfb1c4b21c5d5a43925fbd2d9a082cbb0e8..ece9226253cb04bb012a19c0fa67b7fae1745619 100644 (file)
@@ -546,7 +546,7 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
      * contains ":" the string is evaluated as prefix:name of element. In this case method will look into import map
      * and extract correct ModuleImport. If such import is not present in import map the method will throw {@link YangParseException}
      * <br>
-     * If ModuleImport is present but the value of namespace in ModuleImport is <code>null</code> the method wil throw {@link YangParseException}
+     * If ModuleImport is present but the value of namespace in ModuleImport is <code>null</code> the method will throw {@link YangParseException}
      *
      * @param qnameString QName value as String
      * @param line line in Yang model document where QName occur.
@@ -573,12 +573,12 @@ public final class YangParserListenerImpl extends YangParserBaseListener {
                 }
                 Date revision = imp.getRevision();
                 TreeMap<Date, URI> namespaces = namespaceContext.get(imp.getModuleName());
+                if (namespaces == null) {
+                    throw new YangParseException(moduleName, line, String.format("Imported module %s not found",
+                            imp.getModuleName()));
+                }
                 URI namespace;
                 if (revision == null) {
-                    if (namespaces == null) {
-                        throw new YangParseException(moduleName, line, "imported module " + imp.getModuleName()
-                                + " with prefix " + imp.getPrefix() + " not found.");
-                    }
                     revision = namespaces.lastEntry().getKey();
                     namespace = namespaces.lastEntry().getValue();
                 } else {