From c1dfdbde61e58a5a30337e55ef01129468b16249 Mon Sep 17 00:00:00 2001 From: Tony Tkacik Date: Mon, 15 Sep 2014 10:39:43 +0200 Subject: [PATCH] Bug 1869: Fixed binding-data-codec to work with empty type Empty type is special case, which does not contain value, but has only presence characteristic and in Binding Specification v1 it was converted to boolean (true = present, false or null not present) but new codecs did not have that special handling for empty type which caused some models to fail. Fixed places affected by this bug and added test case which now explicitly tests empty type support. Change-Id: Idec541dc9da987cebcd46ee65d24c17b7cf9a567 Signed-off-by: Tony Tkacik --- .../DataNodeContainerSerializerSource.java | 5 +- .../data/codec/impl/BindingCodecContext.java | 30 +++---- .../data/codec/impl/ValueTypeCodec.java | 38 +++++++++ .../data/codec/test/EmptyLeafTest.java | 82 +++++++++++++++++++ .../opendaylight-yangtools-augment-test.yang | 5 ++ .../AbstractImmutableNormalizedValueNode.java | 6 +- 6 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/EmptyLeafTest.java diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/gen/impl/DataNodeContainerSerializerSource.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/gen/impl/DataNodeContainerSerializerSource.java index 07d6602c2d..9b8faba4cb 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/gen/impl/DataNodeContainerSerializerSource.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/gen/impl/DataNodeContainerSerializerSource.java @@ -7,10 +7,8 @@ */package org.opendaylight.yangtools.binding.data.codec.gen.impl; import com.google.common.base.Preconditions; - import java.util.HashMap; import java.util.Map; - import org.opendaylight.yangtools.binding.data.codec.util.ChoiceDispatchSerializer; import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType; import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature; @@ -31,6 +29,7 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; abstract class DataNodeContainerSerializerSource extends DataObjectSerializerSource { @@ -106,7 +105,7 @@ abstract class DataNodeContainerSerializerSource extends DataObjectSerializerSou while (rootType.getBaseType() != null) { rootType = rootType.getBaseType(); } - if(rootType instanceof BooleanTypeDefinition) { + if(rootType instanceof BooleanTypeDefinition || rootType instanceof EmptyTypeDefinition) { prefix = "is"; } } diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java index dfef41afc0..881a62bc2e 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/BindingCodecContext.java @@ -9,7 +9,6 @@ package org.opendaylight.yangtools.binding.data.codec.impl; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; - import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -25,10 +24,8 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.Callable; - import javax.annotation.Nonnull; import javax.annotation.Nullable; - import org.opendaylight.yangtools.binding.data.codec.impl.NodeCodecContext.CodecContextFactory; import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.concepts.Immutable; @@ -53,6 +50,7 @@ import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; @@ -207,7 +205,7 @@ class BindingCodecContext implements CodecContextFactory, Immutable { while (typeDef.getBaseType() != null) { typeDef = typeDef.getBaseType(); } - if (typeDef instanceof BooleanTypeDefinition) { + if (typeDef instanceof BooleanTypeDefinition || typeDef instanceof EmptyTypeDefinition) { return "is" + suffix; } return GETTER_PREFIX + suffix; @@ -246,6 +244,14 @@ class BindingCodecContext implements CodecContextFactory, Immutable { private Codec getCodec(final Class valueType, final DataSchemaNode schema) { + final TypeDefinition instantiatedType; + if (schema instanceof LeafSchemaNode) { + instantiatedType = ((LeafSchemaNode) schema).getType(); + } else if (schema instanceof LeafListSchemaNode) { + instantiatedType = ((LeafListSchemaNode) schema).getType(); + } else { + throw new IllegalArgumentException("Unsupported leaf node type " + schema.getClass()); + } if (Class.class.equals(valueType)) { @SuppressWarnings({ "unchecked", "rawtypes" }) final Codec casted = (Codec) identityCodec; @@ -254,18 +260,12 @@ class BindingCodecContext implements CodecContextFactory, Immutable { @SuppressWarnings({ "unchecked", "rawtypes" }) final Codec casted = (Codec) instanceIdentifierCodec; return casted; - } else if (BindingReflections.isBindingClass(valueType)) { - final TypeDefinition instantiatedType; - if (schema instanceof LeafSchemaNode) { - instantiatedType = ((LeafSchemaNode) schema).getType(); - } else if (schema instanceof LeafListSchemaNode) { - instantiatedType = ((LeafListSchemaNode) schema).getType(); - } else { - instantiatedType = null; - } - if (instantiatedType != null) { - return getCodec(valueType, instantiatedType); + } else if (Boolean.class.equals(valueType)) { + if(instantiatedType instanceof EmptyTypeDefinition) { + return ValueTypeCodec.EMPTY_CODEC; } + } else if (BindingReflections.isBindingClass(valueType)) { + return getCodec(valueType, instantiatedType); } return ValueTypeCodec.NOOP_CODEC; } diff --git a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ValueTypeCodec.java b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ValueTypeCodec.java index e432c55b00..d8536ee667 100644 --- a/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ValueTypeCodec.java +++ b/code-generator/binding-data-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/ValueTypeCodec.java @@ -15,6 +15,7 @@ import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.yang.binding.util.BindingReflections; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition; /** @@ -59,11 +60,46 @@ abstract class ValueTypeCodec implements Codec { } }; + public static final SchemaUnawareCodec EMPTY_CODEC = new SchemaUnawareCodec() { + + @Override + public Object serialize(Object arg0) { + // Empty type has null value in NormalizedNode and Composite Node + // representation + return null; + } + + @Override + public Object deserialize(Object arg0) { + /* Empty type has boolean.TRUE representation in Binding-aware world + * otherwise it is null / false. + * So when codec is triggered, empty leaf is present, that means we + * are safe to return true. + */ + return Boolean.TRUE; + } + }; + + private static final Callable EMPTY_LOADER = new Callable() { + + @Override + public SchemaUnawareCodec call() throws Exception { + return EMPTY_CODEC; + } + }; + public static SchemaUnawareCodec getCodecFor(final Class typeClz, final TypeDefinition def) { if (BindingReflections.isBindingClass(typeClz)) { return getCachedSchemaUnawareCodec(typeClz, getCodecLoader(typeClz, def)); } + TypeDefinition rootType = def; + while (rootType.getBaseType() != null) { + rootType = rootType.getBaseType(); + } + if(rootType instanceof EmptyTypeDefinition) { + return EMPTY_CODEC; + } return NOOP_CODEC; } @@ -85,6 +121,8 @@ abstract class ValueTypeCodec implements Codec { return EnumerationCodec.loader(typeClz, (EnumTypeDefinition) rootType); } else if (rootType instanceof BitsTypeDefinition) { return BitsCodec.loader(typeClz, (BitsTypeDefinition) rootType); + } else if (rootType instanceof EmptyTypeDefinition) { + return EMPTY_LOADER; } return EncapsulatedValueCodec.loader(typeClz); } diff --git a/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/EmptyLeafTest.java b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/EmptyLeafTest.java new file mode 100644 index 0000000000..53258f8e11 --- /dev/null +++ b/code-generator/binding-data-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/EmptyLeafTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.binding.data.codec.test; + +import static org.junit.Assert.assertTrue; + +import java.util.Collections; +import java.util.Map.Entry; +import javassist.ClassPool; +import org.junit.Test; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.RpcComplexUsesAugmentBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeComplexUsesAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.TreeLeafOnlyAugment; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ContainerWithUsesBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.complex.from.grouping.ListViaUses; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.top.top.level.list.choice.in.list.EmptyLeaf; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.augment.rev140709.top.top.level.list.choice.in.list.EmptyLeafBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.Top; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelList; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListBuilder; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.TopLevelListKey; +import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.binding.rev140701.two.level.list.top.level.list.ChoiceInList; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.sal.binding.generator.util.JavassistUtils; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + + +public class EmptyLeafTest extends AbstractBindingRuntimeTest { + + private static final TopLevelListKey TOP_FOO_KEY = new TopLevelListKey("foo"); + private static final InstanceIdentifier BA_TOP_LEVEL_LIST = InstanceIdentifier.builder(Top.class) + .child(TopLevelList.class, TOP_FOO_KEY).toInstance(); + private static final InstanceIdentifier BA_TREE_LEAF_ONLY = BA_TOP_LEVEL_LIST + .augmentation(TreeLeafOnlyAugment.class); + private static final InstanceIdentifier BA_TREE_COMPLEX_USES = BA_TOP_LEVEL_LIST + .augmentation(TreeComplexUsesAugment.class); + private static final QName SIMPLE_VALUE_QNAME = QName.create(TreeComplexUsesAugment.QNAME, "simple-value"); + + private BindingNormalizedNodeCodecRegistry registry; + + @Override + public void setup() { + super.setup(); + JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void testCaseWithEmptyLeafType() { + TopLevelList withEmptyCase = new TopLevelListBuilder() + .setKey(TOP_FOO_KEY) + .setChoiceInList(new EmptyLeafBuilder().setEmptyType(true).build()) + .build(); + Entry> dom = registry.toNormalizedNode(BA_TOP_LEVEL_LIST, withEmptyCase); + Entry, DataObject> readed = registry.fromNormalizedNode(dom.getKey(),dom.getValue()); + ChoiceInList list = ((TopLevelList) readed.getValue()).getChoiceInList(); + assertTrue(list instanceof EmptyLeaf); + assertTrue(((EmptyLeaf) list).isEmptyType()); + } + + private RpcComplexUsesAugment createComplexData() { + return new RpcComplexUsesAugmentBuilder() + .setContainerWithUses(new ContainerWithUsesBuilder() + .setLeafFromGrouping("foo") + .build()) + .setListViaUses(Collections.emptyList()) + .build(); + } + +} diff --git a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang index 6d155becc7..7363f38c46 100644 --- a/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang +++ b/code-generator/binding-test-model/src/main/yang/opendaylight-yangtools-augment-test.yang @@ -97,6 +97,11 @@ module opendaylight-yangtools-augment-test { case complex-via-uses { uses complex-from-grouping; } + case empty-leaf { + leaf empty-type { + type empty; + } + } } augment "/test:put-top/test:input/test:top-level-list/test:choice-in-list" { diff --git a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java index beb797975c..bd8252159a 100644 --- a/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java +++ b/yang/yang-data-impl/src/main/java/org/opendaylight/yangtools/yang/data/impl/schema/nodes/AbstractImmutableNormalizedValueNode.java @@ -20,7 +20,11 @@ public abstract class AbstractImmutableNormalizedValueNode