Bug 5446: toString() throws exception for 'type binary' binding 72/38272/3
authorPeter Kajsa <pkajsa@cisco.com>
Mon, 2 May 2016 12:07:26 +0000 (14:07 +0200)
committerPeter Kajsa <pkajsa@cisco.com>
Wed, 4 May 2016 07:30:41 +0000 (07:30 +0000)
Binding UnionTypeCodec invokes toString method on byte[] array
by its deserialization which leads to undesirable results.
The same problem occurs also in generated java class of Union type.

Change-Id: I61b7500541a1dd86ba7c39377113a12eb270a274
Signed-off-by: Peter Kajsa <pkajsa@cisco.com>
binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/yangtools/binding/data/codec/impl/UnionTypeCodec.java
binding/mdsal-binding-dom-codec/src/test/java/org/opendaylight/yangtools/binding/data/codec/test/UnionTypeTest.java
binding/mdsal-binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/UnionTemplate.xtend
binding/mdsal-binding-test-model/src/main/java/org/opendaylight/yang/gen/v1/bug5446/rev151105/IpAddressBinaryBuilder.java [new file with mode: 0644]
binding/mdsal-binding-test-model/src/main/yang/bug5446.yang [new file with mode: 0644]

index cc063580c1f8d5598213b2b656d0da229ecbf1b3..c483fe7f25cfa169b32e768dd718bdca9d6ec7fb 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.binding.data.codec.impl;
 
 import com.google.common.collect.ImmutableSet;
+import com.google.common.io.BaseEncoding;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -66,7 +67,11 @@ final class UnionTypeCodec extends ReflectionBasedCodec {
     @Override
     public Object deserialize(final Object input) {
         try {
-            return charConstructor.newInstance((input.toString().toCharArray()));
+            if (input instanceof byte[]) {
+                return charConstructor.newInstance(BaseEncoding.base64().encode((byte[]) input).toCharArray());
+            } else {
+                return charConstructor.newInstance((input.toString().toCharArray()));
+            }
         } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
             throw new IllegalStateException("Could not construct instance",e);
         }
index e5198925b6027a821931422be340b6ceca327b97..eeb9333582b9b9a6d2ede3ca30d4cefb6992ab85 100644 (file)
@@ -8,9 +8,17 @@
 
 package org.opendaylight.yangtools.binding.data.codec.test;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map.Entry;
 import javassist.ClassPool;
 import org.junit.Assert;
 import org.junit.Test;
+import org.opendaylight.yang.gen.v1.bug5446.rev151105.IpAddressBinary;
+import org.opendaylight.yang.gen.v1.bug5446.rev151105.IpAddressBinaryBuilder;
+import org.opendaylight.yang.gen.v1.bug5446.rev151105.Root;
+import org.opendaylight.yang.gen.v1.bug5446.rev151105.RootBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.union.rev150121.TopLevel;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.union.rev150121.TopLevelBuilder;
 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtools.test.union.rev150121.Wrapper;
@@ -18,6 +26,7 @@ import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.yangtool
 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;
@@ -55,4 +64,20 @@ public class UnionTypeTest extends AbstractBindingRuntimeTest {
                 .build();
         Assert.assertEquals(topLevelEntry, containerNode);
     }
+
+    @Test
+    public void bug5446Test() {
+        IpAddressBinary ipAddress = IpAddressBinaryBuilder.getDefaultInstance("fwAAAQ==");
+        Root root = new RootBuilder().setIpAddress(ipAddress).build();
+        NormalizedNode<?, ?> rootNode = registry.toNormalizedNode(InstanceIdentifier.builder(Root.class).build(), root)
+                .getValue();
+
+        Entry<InstanceIdentifier<?>, DataObject> rootEntry = registry.fromNormalizedNode(
+                YangInstanceIdentifier.of(rootNode.getNodeType()), rootNode);
+
+        DataObject rootObj = rootEntry.getValue();
+        assertTrue(rootObj instanceof Root);
+        IpAddressBinary desIpAddress = ((Root) rootObj).getIpAddress();
+        assertEquals(ipAddress, desIpAddress);
+    }
 }
index e65fa9fcf5cf95c73238ba4b0bd003b1dbfe8c6b..d356c28ee02457ba30b803b02911665e3c628226 100644 (file)
@@ -24,6 +24,23 @@ class UnionTemplate extends ClassTemplate {
      */
     new(GeneratedTransferObject genType) {
         super(genType)
+        if(isBaseEncodingImportRequired) {
+            this.importMap.put("BaseEncoding","com.google.common.io")
+        }
+    }
+
+    final private def boolean isBaseEncodingImportRequired() {
+        for (property : finalProperties) {
+            val propRet = property.returnType
+            if (propRet instanceof GeneratedTransferObject && (propRet as GeneratedTransferObject).typedef &&
+                (propRet as GeneratedTransferObject).properties != null &&
+                !(propRet as GeneratedTransferObject).properties.empty &&
+                ((propRet as GeneratedTransferObject).properties.size == 1) &&
+                (propRet as GeneratedTransferObject).properties.get(0).name.equals("value") &&
+                "byte[]".equals((propRet as GeneratedTransferObject).properties.get(0).returnType.name)) {
+                return true;
+            }
+        }
     }
 
     override constructors() '''
@@ -93,6 +110,15 @@ class UnionTemplate extends ClassTemplate {
                                         && BOOLEAN.equals((propRet as GeneratedTransferObject).properties.get(0).returnType)» // And the property value is of type boolean
                                     ««« generated boolean typedef
                                     this.«other.fieldName» = «property.fieldName».isValue().toString().toCharArray();
+                                «ELSEIF propRet instanceof GeneratedTransferObject // Is it a GeneratedTransferObject
+                                        && (propRet as GeneratedTransferObject).typedef  // Is it a typedef
+                                        && (propRet as GeneratedTransferObject).properties != null
+                                        && !(propRet as GeneratedTransferObject).properties.empty
+                                        && ((propRet as GeneratedTransferObject).properties.size == 1)
+                                        && (propRet as GeneratedTransferObject).properties.get(0).name.equals("value")
+                                        && "byte[]".equals((propRet as GeneratedTransferObject).properties.get(0).returnType.name)»
+                                    ««« generated byte[] typedef
+                                    this.«other.fieldName» = BaseEncoding.base64().encode(«property.fieldName».getValue()).toCharArray();
                                 «ELSE»
                                     ««« generated type
                                     this.«other.fieldName» = «property.fieldName».getValue().toString().toCharArray();
diff --git a/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/yang/gen/v1/bug5446/rev151105/IpAddressBinaryBuilder.java b/binding/mdsal-binding-test-model/src/main/java/org/opendaylight/yang/gen/v1/bug5446/rev151105/IpAddressBinaryBuilder.java
new file mode 100644 (file)
index 0000000..05f163a
--- /dev/null
@@ -0,0 +1,25 @@
+package org.opendaylight.yang.gen.v1.bug5446.rev151105;
+
+/**
+ * The purpose of generated class in src/main/java for Union types is to create new instances of unions from a string representation.
+ * In some cases it is very difficult to automate it since there can be unions such as (uint32 - uint16), or (string - uint32).
+ *
+ * The reason behind putting it under src/main/java is:
+ * This class is generated in form of a stub and needs to be finished by the user. This class is generated only once to prevent
+ * loss of user code.
+ *
+ */
+public class IpAddressBinaryBuilder {
+    public static IpAddressBinary getDefaultInstance(java.lang.String defaultValue) {
+        return new IpAddressBinary(Ipv4AddressBinary.getDefaultInstance(defaultValue));
+    }
+
+    public static IpAddressBinary getDefaultInstance(byte[] defaultValue) {
+        if (defaultValue.length == 4) {
+            return new IpAddressBinary(new Ipv4AddressBinary(defaultValue));
+        } else if (defaultValue.length == 16) {
+            return new IpAddressBinary(new Ipv6AddressBinary(defaultValue));
+        }
+        return null;
+    }
+}
diff --git a/binding/mdsal-binding-test-model/src/main/yang/bug5446.yang b/binding/mdsal-binding-test-model/src/main/yang/bug5446.yang
new file mode 100644 (file)
index 0000000..e0f18a6
--- /dev/null
@@ -0,0 +1,33 @@
+module bug5446 {
+    yang-version 1;
+    namespace "bug5446";
+    prefix "bug5446";
+
+    revision "2015-11-05" {
+    }
+
+    typedef ipv4-address-binary {
+        type binary {
+            length "4";
+        }
+    }
+
+    typedef ipv6-address-binary {
+        type binary {
+            length "16";
+        }
+    }
+
+    typedef ip-address-binary {
+        type union {
+            type ipv4-address-binary;
+            type ipv6-address-binary;
+        }
+    }
+    
+    container root {
+        leaf ip-address {
+            type ip-address-binary;
+        }
+    }
+}