From 55943efa7add3ba1223427d8ba5c1e4c59c837b5 Mon Sep 17 00:00:00 2001 From: Jakub Toth Date: Fri, 21 Jul 2017 11:58:08 +0200 Subject: [PATCH] Bug 8449 - BindingToNormalizedNodeCodec fails to deserialize union of leafrefs Fix problem of leafref in typedef called from union *generated part *generating of new property of GTO for leaf's union type of typedef according to return type of referenced leaf via leafref from typedef *codec part *getting codec of leaf type according to new generator part of leafref in typedef *tests Change-Id: Ibffe4e51ef66f1911c32c71d4f08bbdbdd40e234 Signed-off-by: Jakub Toth --- .../data/codec/impl/UnionTypeCodec.java | 77 +++++++++++++++-- .../test/LeafrefSerializeDeserializeTest.java | 83 +++++++++++++++++++ .../src/main/yang/bug8449.yang | 60 ++++++++++++++ 3 files changed, 213 insertions(+), 7 deletions(-) create mode 100644 binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/LeafrefSerializeDeserializeTest.java create mode 100644 binding/mdsal-binding-test-model/src/main/yang/bug8449.yang diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java index ee161a9b53..4dac099d1f 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java @@ -12,10 +12,18 @@ import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.Set; import java.util.concurrent.Callable; +import org.opendaylight.mdsal.binding.yang.types.BaseYangTypes; import org.opendaylight.yangtools.concepts.Codec; import org.opendaylight.yangtools.yang.binding.BindingMapping; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition; import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; final class UnionTypeCodec extends ReflectionBasedCodec { private final ImmutableSet typeCodecs; @@ -29,19 +37,74 @@ final class UnionTypeCodec extends ReflectionBasedCodec { final BindingCodecContext bindingCodecContext) { return () -> { final Set values = new LinkedHashSet<>(); - for (TypeDefinition subtype : unionType.getTypes()) { - Method valueGetter = unionCls.getMethod("get" + BindingMapping.getClassName(subtype.getQName())); - Class valueType = valueGetter.getReturnType(); - Codec valueCodec = bindingCodecContext.getCodec(valueType, subtype); - values.add(new UnionValueOptionContext(unionCls, valueType, valueGetter, valueCodec)); + for (final TypeDefinition subtype : unionType.getTypes()) { + if (subtype instanceof LeafrefTypeDefinition) { + addLeafrefValueCodec(unionCls, unionType, bindingCodecContext, values, subtype); + } else { + final Method valueGetter = + unionCls.getMethod("get" + BindingMapping.getClassName(subtype.getQName())); + final Class valueType = valueGetter.getReturnType(); + final Codec valueCodec = bindingCodecContext.getCodec(valueType, subtype); + + values.add(new UnionValueOptionContext(unionCls, valueType, valueGetter, valueCodec)); + } } + return new UnionTypeCodec(unionCls, values); }; } + /** + * Prepare codec for type from leaf's return type of leafref. + * + * @param unionCls + * - union class + * @param unionType + * - union type + * @param bindingCodecContext + * - binding codec context + * @param values + * - union values + * @param subtype + * - subtype of union + * @throws NoSuchMethodException + */ + private static void addLeafrefValueCodec(final Class unionCls, final UnionTypeDefinition unionType, + final BindingCodecContext bindingCodecContext, final Set values, + final TypeDefinition subtype) throws NoSuchMethodException { + final SchemaContext schemaContext = bindingCodecContext.getRuntimeContext().getSchemaContext(); + final Module module = schemaContext.findModuleByNamespaceAndRevision(subtype.getQName().getNamespace(), + subtype.getQName().getRevision()); + final RevisionAwareXPath xpath = ((LeafrefTypeDefinition) subtype).getPathStatement(); + // find schema node in schema context by xpath of leafref + final SchemaNode dataNode; + if (xpath.isAbsolute()) { + dataNode = SchemaContextUtil.findDataSchemaNode(schemaContext, module, xpath); + } else { + dataNode = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, module, unionType, xpath); + } + final String className = BindingMapping.getClassName(unionCls.getSimpleName()); + final LeafSchemaNode typeNode = (LeafSchemaNode) dataNode; + + // prepare name of type form return type of referenced leaf + final String typeName = BindingMapping.getClassName(BaseYangTypes.BASE_YANG_TYPES_PROVIDER + .javaTypeForSchemaDefinitionType(typeNode.getType(), typeNode).getName()); + + // get method via reflection from generated code according to + // get_TypeName_Value method + final Method valueGetterParent = unionCls + .getMethod(new StringBuilder("get").append(typeName).append(className).append("Value").toString()); + final Class returnType = valueGetterParent.getReturnType(); + + // prepare codec of union subtype according to return type of referenced + // leaf + final Codec valueCodec = bindingCodecContext.getCodec(returnType, subtype); + values.add(new UnionValueOptionContext(unionCls, returnType, valueGetterParent, valueCodec)); + } + @Override public Object deserialize(final Object input) { - for (UnionValueOptionContext member : typeCodecs) { + for (final UnionValueOptionContext member : typeCodecs) { final Object ret = member.deserializeUnion(input); if (ret != null) { return ret; @@ -55,7 +118,7 @@ final class UnionTypeCodec extends ReflectionBasedCodec { @Override public Object serialize(final Object input) { if (input != null) { - for (UnionValueOptionContext valCtx : typeCodecs) { + for (final UnionValueOptionContext valCtx : typeCodecs) { final Object domValue = valCtx.serialize(input); if (domValue != null) { return domValue; diff --git a/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/LeafrefSerializeDeserializeTest.java b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/LeafrefSerializeDeserializeTest.java new file mode 100644 index 0000000000..c50e08283e --- /dev/null +++ b/binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/LeafrefSerializeDeserializeTest.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.assertEquals; +import static org.junit.Assert.assertNotNull; +import java.util.Map.Entry; +import javassist.ClassPool; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.mdsal.binding.generator.util.JavassistUtils; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.Cont; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.Cont.Ref; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.ContBuilder; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.ContInt32; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.ContInt32.RefUnionInt32; +import org.opendaylight.yang.gen.v1.bug8449.rev180405.ContInt32Builder; +import org.opendaylight.yangtools.binding.data.codec.gen.impl.StreamWriterGenerator; +import org.opendaylight.yangtools.binding.data.codec.impl.BindingNormalizedNodeCodecRegistry; +import org.opendaylight.yangtools.yang.binding.DataObject; +import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; +import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; + +public class LeafrefSerializeDeserializeTest extends AbstractBindingRuntimeTest { + + private BindingNormalizedNodeCodecRegistry registry; + + @Override + @Before + public void setup() { + super.setup(); + final JavassistUtils utils = JavassistUtils.forClassPool(ClassPool.getDefault()); + this.registry = new BindingNormalizedNodeCodecRegistry(StreamWriterGenerator.create(utils)); + this.registry.onBindingRuntimeContextUpdated(getRuntimeContext()); + } + + @Test + public void listReferenceTest() { + final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(Cont.QNAME).build(); + final InstanceIdentifier fromYangInstanceIdentifier = this.registry.fromYangInstanceIdentifier(contYII); + assertNotNull(fromYangInstanceIdentifier); + + final InstanceIdentifier BA_II_CONT = InstanceIdentifier.builder(Cont.class).build(); + final Ref refVal = new Ref("myvalue"); + final Cont data = new ContBuilder().setRef(refVal).build(); + final Entry> normalizedNode = + this.registry.toNormalizedNode(BA_II_CONT, data); + assertNotNull(normalizedNode); + + final Entry, DataObject> fromNormalizedNode = + this.registry.fromNormalizedNode(contYII, normalizedNode.getValue()); + assertNotNull(fromNormalizedNode); + final Cont value = (Cont) fromNormalizedNode.getValue(); + assertEquals(refVal, value.getRef()); + } + + @Test + public void uint32LeafrefTest() { + final YangInstanceIdentifier contYII = YangInstanceIdentifier.builder().node(ContInt32.QNAME).build(); + final InstanceIdentifier fromYangInstanceIdentifier = this.registry.fromYangInstanceIdentifier(contYII); + assertNotNull(fromYangInstanceIdentifier); + + final InstanceIdentifier BA_II_CONT = InstanceIdentifier.builder(ContInt32.class).build(); + final RefUnionInt32 refVal = new RefUnionInt32(5l); + final ContInt32 data = new ContInt32Builder().setRefUnionInt32(refVal).build(); + final Entry> normalizedNode = + this.registry.toNormalizedNode(BA_II_CONT, data); + assertNotNull(normalizedNode); + + final Entry, DataObject> fromNormalizedNode = + this.registry.fromNormalizedNode(contYII, normalizedNode.getValue()); + assertNotNull(fromNormalizedNode); + final ContInt32 value = (ContInt32) fromNormalizedNode.getValue(); + assertEquals(refVal, value.getRefUnionInt32()); + } +} + diff --git a/binding/mdsal-binding-test-model/src/main/yang/bug8449.yang b/binding/mdsal-binding-test-model/src/main/yang/bug8449.yang new file mode 100644 index 0000000000..b965242dcf --- /dev/null +++ b/binding/mdsal-binding-test-model/src/main/yang/bug8449.yang @@ -0,0 +1,60 @@ +module bug8449 { + yang-version 1; + namespace "bug8449"; + prefix "tst"; + + revision "2017-16-05" { + } + + typedef name1-ref { + type leafref { + path "/tst:top/tst:name1"; + } + } + + typedef name2-ref { + type leafref { + path "/tst:top/tst:name2"; + } + } + + typedef int32-ref { + type leafref { + path "/tst:top-int/tst:leaf-int32"; + } + } + + container top-int { + leaf leaf-int32 { + type uint32; + } + } + + container top { + leaf name1 { + type string; + } + leaf name2 { + type string; + } + } + + container cont-int32 { + leaf ref-union-int32 { + type union { + type int32-ref; + } + } + } + + container cont { + leaf ref { + type union { + type name1-ref; + type name2-ref; + } + mandatory true; + } + } +} + -- 2.36.6