From: Lukas Sedlak Date: Thu, 21 Aug 2014 09:40:51 +0000 (+0000) Subject: Merge "add new tests BindingTypesTest, BaseYangTypesTest" X-Git-Tag: release/helium~188 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=77121306ff45afed6ddb8fd67a930c1152319d33;hp=dfe61b5f5311cbd89952bf30387bbd571b2a7d67;p=yangtools.git Merge "add new tests BindingTypesTest, BaseYangTypesTest" --- diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java index dcaecd34c8..700476f86b 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ChoiceNodeCodecContext.java @@ -11,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 @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> getPossibleStreamChild(final Class childClass) { + DataContainerCodecPrototype child = byClass.get(childClass); + if(child != null) { + return Optional.>of(child.get()); + } + return Optional.absent(); } Iterable> getCaseChildrenClasses() { diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataContainerCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataContainerCodecContext.java index 6b1124c30e..e873126598 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataContainerCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataContainerCodecContext.java @@ -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 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> getPossibleStreamChild(final Class childClass); @Override public String toString() { diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java index 321f8ded5b..32fdf6d80e 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/DataObjectCodecContext.java @@ -10,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 extends DataC return childProto.get(); } + @Override + protected Optional> getPossibleStreamChild(final Class childClass) { + DataContainerCodecPrototype childProto = byStreamClass.get(childClass); + if(childProto != null) { + return Optional.>of(childProto.get()); + } + return Optional.absent(); + } + @Override protected DataContainerCodecContext getIdentifierChild(final InstanceIdentifier.PathArgument arg, final List builder) { diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java index d7ede0fbb8..c86585c7e0 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/LazyDataObject.java @@ -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> augData = data.getChild(augCtx.getDomPathArgument()); - if (augData.isPresent()) { - return augCtx.dataFromNormalizedNode(augData.get()); + Preconditions.checkNotNull(cls,"Supplied augmentation must not be null."); + final Optional> augCtx= context.getPossibleStreamChild(cls); + if(augCtx.isPresent()) { + final Optional> augData = data.getChild(augCtx.get().getDomPathArgument()); + if (augData.isPresent()) { + return augCtx.get().dataFromNormalizedNode(augData.get()); + } } return null; } diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java index 1a57b21623..be328ca199 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/SchemaRootCodecContext.java @@ -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 { return children.getUnchecked(childClass); } + @Override + protected Optional> getPossibleStreamChild(final Class childClass) { + throw new UnsupportedOperationException("Not supported"); + } + @Override protected YangInstanceIdentifier.PathArgument getDomPathArgument() { throw new UnsupportedOperationException(); diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java index d657087326..9c66f8d6a6 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/BindingGeneratorImpl.java @@ -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); diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend index 396001dbd3..98d7ef1d9e 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/YangTemplate.xtend @@ -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 diff --git a/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend b/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend index 4a78be34a8..d8c4959296 100644 --- a/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend +++ b/code-generator/maven-sal-api-gen-plugin/src/main/java/org/opendaylight/yangtools/yang/unified/doc/generator/GeneratorImpl.xtend @@ -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 nodes = new ArrayList(); 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 '''«text»''' - } else if(!prefix.nullOrEmpty){ - val String module = imports.get(prefix) - if(!module.nullOrEmpty) { - return '''«prefix»:«text»''' - //to enable external (import) links - //return '''«prefix»:«text»''' - } + } else { + return '''(«ns»)«text»''' + //to enable external (import) links + //return '''«prefix»:«text»''' } - 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) diff --git a/common/features/pom.xml b/common/features/pom.xml index 8ce171760d..4fe4b110c2 100644 --- a/common/features/pom.xml +++ b/common/features/pom.xml @@ -238,6 +238,7 @@ org.opendaylight.yangtools.thirdparty xtend-lib-osgi + compile diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java index 6a7d67e3ad..6667076fb4 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/NormalizedNodeContainerModificationStrategy.java @@ -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 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 modifications) { for (ModifiedNode mod : modifications) { - final PathArgument id = mod.getIdentifier(); + final YangInstanceIdentifier.PathArgument id = mod.getIdentifier(); final Optional cm = meta.getChild(id); Optional 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 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 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 childNodes; + private final Map childNodes; public ChoiceModificationStrategy(final ChoiceNode schemaNode) { super(org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode.class); - ImmutableMap.Builder child = ImmutableMap.builder(); + ImmutableMap.Builder 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 getChild(final PathArgument child) { + public Optional getChild(final YangInstanceIdentifier.PathArgument child) { return Optional.fromNullable(childNodes.get(child)); } @@ -244,8 +231,8 @@ abstract class NormalizedNodeContainerModificationStrategy extends SchemaAwareAp } @Override - public Optional getChild(final PathArgument identifier) { - if (identifier instanceof NodeWithValue) { + public Optional 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 getChild(final PathArgument identifier) { - if (identifier instanceof NodeIdentifierWithPredicates) { + public Optional 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 getChild(final PathArgument identifier) { - if (identifier instanceof NodeWithValue) { + public Optional 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 getChild(final PathArgument identifier) { - if (identifier instanceof NodeIdentifierWithPredicates) { + public Optional getChild(final YangInstanceIdentifier.PathArgument identifier) { + if (identifier instanceof YangInstanceIdentifier.NodeIdentifierWithPredicates) { return entryStrategy; } return Optional.absent(); diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java index 915f2fbf87..d0a0bcd33f 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/SchemaAwareApplyOperation.java @@ -9,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 index 0000000000..7525dc348b --- /dev/null +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/tree/ConcurrentTreeModificationTest.java @@ -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 assertPresentAndType(final Optional potential, final Class 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> 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> 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()); + } +} diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java index 5b5fbbfb1c..ece9226253 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangParserListenerImpl.java @@ -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} *
- * If ModuleImport is present but the value of namespace in ModuleImport is null the method wil throw {@link YangParseException} + * If ModuleImport is present but the value of namespace in ModuleImport is null 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 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 {