BUG-1276: fixed generated union constructor 06/9006/2
authorMartin Vitez <mvitez@cisco.com>
Wed, 9 Jul 2014 14:17:59 +0000 (16:17 +0200)
committerMartin Vitez <mvitez@cisco.com>
Tue, 15 Jul 2014 11:30:53 +0000 (13:30 +0200)
Current generated definition of union constructor

public UnionType(Arg1 _arg1) {
  super();
  this._arg1 = _arg1;
  this._arg2 = null;
  this._value = null;
}

was incorrect and setting this._value == null was replaced with setting _value to correct value, for example:

this._value = arg1.getValue()
or
this._value = _arg1.getValue().toString().toCharArray()

Change-Id: I837e5e1fc6845937d19abcdc8e2f8d52b410d316
Signed-off-by: Martin Vitez <mvitez@cisco.com>
code-generator/binding-java-api-generator/src/main/java/org/opendaylight/yangtools/sal/java/api/generator/UnionTemplate.xtend
code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/Bug1276Test.java [new file with mode: 0644]
code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/TypedefCompilationTest.java
code-generator/binding-java-api-generator/src/test/resources/compilation/bug1276/foo.yang [new file with mode: 0644]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/builder/impl/TypeUtils.java

index f9c27fe86529d2f8175dc2c1f45805b986f881b2..94cfe1e8d05cec8076711637a6da1129e06d8fed 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.sal.java.api.generator
 
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
 import java.beans.ConstructorProperties
+import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
 
 /**
  * Template for generating JAVA class. 
@@ -37,14 +38,15 @@ class UnionTemplate extends ClassTemplate {
 
     private def unionConstructors() '''
         «FOR property : finalProperties SEPARATOR "\n"»
-            «val isCharArray = "char[]".equals(property.returnType.name)»
+            «val propRet = property.returnType»
+            «val isCharArray = "char[]".equals(propRet.name)»
             «IF isCharArray»
                 /**
                  * Constructor provided only for using in JMX. Don't use it for
                  * construction new object of this union type. 
                  */
                 @«ConstructorProperties.importedName»("«property.name»")
-                public «type.name»(«property.returnType.importedName» «property.fieldName») {
+                public «type.name»(«propRet.importedName» «property.fieldName») {
                     «String.importedName» defVal = new «String.importedName»(«property.fieldName»);
                     «type.name» defInst = «type.name»Builder.getDefaultInstance(defVal);
                     «FOR other : finalProperties»
@@ -61,7 +63,28 @@ class UnionTemplate extends ClassTemplate {
                     super(«parentProperties.asArguments»);
                     this.«property.fieldName» = «property.fieldName»;
                     «FOR other : finalProperties»
-                        «IF property != other»this.«other.fieldName» = null;«ENDIF»
+                        «IF property != other»
+                            «IF "value".equals(other.name)»
+                                «IF "java.lang.String".equals(propRet.fullyQualifiedName)»
+                                    ««« type string
+                                    this.«other.fieldName» = «property.fieldName».toCharArray();
+                                «ELSEIF "byte[]".equals(propRet.name)»
+                                    ««« type binary
+                                    this.«other.fieldName» = new «String.importedName»(«property.fieldName»).toCharArray();
+                                «ELSEIF propRet.fullyQualifiedName.startsWith("java.lang") || propRet instanceof Enumeration»
+                                    ««« type int*, uint or enumeration*
+                                    this.«other.fieldName» = «property.fieldName».toString().toCharArray();
+                                «ELSEIF propRet instanceof GeneratedTransferObject && (propRet as GeneratedTransferObject).unionType»
+                                    ««« union type
+                                    this.«other.fieldName» = «property.fieldName».getValue();
+                                «ELSE»
+                                    ««« generated type
+                                    this.«other.fieldName» = «property.fieldName».getValue().toString().toCharArray();
+                                «ENDIF»
+                            «ELSE»
+                                this.«other.fieldName» = null;
+                            «ENDIF»
+                        «ENDIF»
                     «ENDFOR»
                 }
             «ENDIF»
diff --git a/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/Bug1276Test.java b/code-generator/binding-java-api-generator/src/test/java/org/opendaylight/yangtools/sal/java/api/generator/test/Bug1276Test.java
new file mode 100644 (file)
index 0000000..d2653f2
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2013 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.sal.java.api.generator.test;
+
+import static org.junit.Assert.assertTrue;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.BASE_PKG;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.COMPILER_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.FS;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.GENERATOR_OUTPUT_PATH;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.assertContainsConstructor;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.cleanUp;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.getSourceFiles;
+import static org.opendaylight.yangtools.sal.java.api.generator.test.CompilationTestUtils.testCompilation;
+
+import java.io.File;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import org.junit.Test;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
+import org.opendaylight.yangtools.sal.java.api.generator.GeneratorJavaFile;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * Previous construction of union constructor
+ *
+ * <code>
+ * public IpAddress(Arg1 _arg1) {
+ *     super();
+ *     this._arg1 = _arg1;
+ *     this._arg2 = null;
+ *     this._value = null;
+ * }
+ * </code>
+ *
+ * was incorrect and setting
+ *
+ * <code>this._value == null</code>
+ *
+ * was replaced with setting _value to correct value, for example:
+ *
+ * <code>this._value = arg1.getValue()</code> or
+ * <code>this._value = _arg1.getValue().toString().toCharArray()</code>
+ *
+ */
+public class Bug1276Test extends BaseCompilationTest {
+
+    @Test
+    public void test() throws Exception {
+        final File sourcesOutputDir = new File(GENERATOR_OUTPUT_PATH + FS + "bug1276");
+        assertTrue("Failed to create test file '" + sourcesOutputDir + "'", sourcesOutputDir.mkdir());
+        final File compiledOutputDir = new File(COMPILER_OUTPUT_PATH + FS + "bug1276");
+        assertTrue("Failed to create test file '" + compiledOutputDir + "'", compiledOutputDir.mkdir());
+
+        generateTestSources("/compilation/bug1276", sourcesOutputDir);
+
+        // Test if sources are compilable
+        testCompilation(sourcesOutputDir, compiledOutputDir);
+
+        ClassLoader loader = new URLClassLoader(new URL[] { compiledOutputDir.toURI().toURL() });
+        Class<?> ipAddressClass = Class.forName(BASE_PKG + ".test.yang.union.rev140715.IpAddress", true, loader);
+        Class<?> ipv4AddressClass = Class.forName(BASE_PKG + ".test.yang.union.rev140715.Ipv4Address", true, loader);
+        Class<?> hostClass = Class.forName(BASE_PKG + ".test.yang.union.rev140715.Host", true, loader);
+
+        Constructor<?> ipAddressConstructor = assertContainsConstructor(ipAddressClass, ipv4AddressClass);
+        Constructor<?> ipv4addressConstructor = assertContainsConstructor(ipv4AddressClass, String.class);
+        Constructor<?> hostConstructor = assertContainsConstructor(hostClass, ipAddressClass);
+
+        // test IpAddress with Ipv4Address argument
+        Object ipv4address = ipv4addressConstructor.newInstance("192.168.0.1");
+        Object ipAddress = ipAddressConstructor.newInstance(ipv4address);
+        Method getValue = ipAddressClass.getDeclaredMethod("getValue");
+        char[] expected = "192.168.0.1".toCharArray();
+        Object actual = getValue.invoke(ipAddress);
+        assertTrue(actual instanceof char[]);
+        assertTrue(Arrays.equals(expected, (char[]) actual));
+
+        // test Host with IpAddress argument
+        Object host = hostConstructor.newInstance(ipAddress);
+        getValue = hostClass.getDeclaredMethod("getValue");
+        actual = getValue.invoke(host);
+        assertTrue(actual instanceof char[]);
+        assertTrue(Arrays.equals(expected, (char[]) actual));
+
+        cleanUp(sourcesOutputDir, compiledOutputDir);
+    }
+
+    private void generateTestSources(String resourceDirPath, File sourcesOutputDir) throws Exception {
+        final List<File> sourceFiles = getSourceFiles(resourceDirPath);
+        final SchemaContext context = parser.parseFiles(sourceFiles);
+        final List<Type> types = bindingGenerator.generateTypes(context);
+        final GeneratorJavaFile generator = new GeneratorJavaFile(new HashSet<>(types));
+        generator.generateToFile(sourcesOutputDir);
+    }
+
+}
index f8b2eda1a662f0831e56393b2011b7cc9cd14e35..f310d5a487ecca8905cca2227f3db53e1235d673 100644 (file)
@@ -228,7 +228,8 @@ public class TypedefCompilationTest extends BaseCompilationTest {
 
         // typedef string-ext3
         assertFalse(stringExt3Class.isInterface());
-        assertContainsFieldWithValue(stringExt3Class, "serialVersionUID", Long.TYPE, -2751063130555484180L, String.class);
+        assertContainsFieldWithValue(stringExt3Class, "serialVersionUID", Long.TYPE, -2751063130555484180L,
+                String.class);
         assertEquals(1, stringExt3Class.getDeclaredFields().length);
         expectedConstructor = assertContainsConstructor(stringExt3Class, String.class);
         assertContainsConstructor(stringExt3Class, stringExt3Class);
@@ -244,7 +245,8 @@ public class TypedefCompilationTest extends BaseCompilationTest {
         assertFalse(myDecimalTypeClass.isInterface());
         assertContainsField(myDecimalTypeClass, VAL, BigDecimal.class);
         assertContainsField(myDecimalTypeClass, RANGE, List.class);
-        assertContainsFieldWithValue(myDecimalTypeClass, "serialVersionUID", Long.TYPE, 3143735729419861095L, BigDecimal.class);
+        assertContainsFieldWithValue(myDecimalTypeClass, "serialVersionUID", Long.TYPE, 3143735729419861095L,
+                BigDecimal.class);
         assertEquals(3, myDecimalTypeClass.getDeclaredFields().length);
         assertContainsMethod(myDecimalTypeClass, BigDecimal.class, "getValue");
         expectedConstructor = assertContainsConstructor(myDecimalTypeClass, BigDecimal.class);
@@ -268,7 +270,8 @@ public class TypedefCompilationTest extends BaseCompilationTest {
         assertFalse(unionExt1Class.isInterface());
         assertContainsField(unionExt1Class, "_int16", Short.class);
         assertContainsField(unionExt1Class, "_int32", Integer.class);
-        assertContainsFieldWithValue(unionExt1Class, "serialVersionUID", Long.TYPE, -5610530488718168882L, Short.class);
+        assertContainsFieldWithValue(unionExt1Class, "serialVersionUID", Long.TYPE, -5610530488718168882L,
+                new Class<?>[] { Short.class }, Short.valueOf("1"));
         assertEquals(4, unionExt1Class.getDeclaredFields().length);
         assertContainsMethod(unionExt1Class, Short.class, "getInt16");
         assertContainsMethod(unionExt1Class, Integer.class, "getInt32");
@@ -280,7 +283,8 @@ public class TypedefCompilationTest extends BaseCompilationTest {
 
         // typedef union-ext2
         assertFalse(unionExt2Class.isInterface());
-        assertContainsFieldWithValue(unionExt2Class, "serialVersionUID", Long.TYPE, -8833407459073585206L, Short.class);
+        assertContainsFieldWithValue(unionExt2Class, "serialVersionUID", Long.TYPE, -8833407459073585206L,
+                new Class<?>[] { Short.class }, Short.valueOf("1"));
         assertEquals(1, unionExt2Class.getDeclaredFields().length);
         assertEquals(0, unionExt2Class.getDeclaredMethods().length);
         assertContainsConstructor(unionExt2Class, Short.class);
@@ -293,8 +297,10 @@ public class TypedefCompilationTest extends BaseCompilationTest {
         assertFalse(unionExt3Class.isInterface());
         assertContainsField(unionExt3Class, "_string", String.class);
         assertContainsField(unionExt3Class, "_unionExt2", unionExt2Class);
-        assertContainsFieldWithValue(unionExt3Class, UNITS, String.class, "object id", String.class);
-        assertContainsFieldWithValue(unionExt3Class, "serialVersionUID", Long.TYPE, 4347887914884631036L, String.class);
+        assertContainsFieldWithValue(unionExt3Class, UNITS, String.class, "object id", new Class<?>[] { String.class },
+                "");
+        assertContainsFieldWithValue(unionExt3Class, "serialVersionUID", Long.TYPE, 4347887914884631036L,
+                new Class<?>[] { String.class }, "");
         assertEquals(5, unionExt3Class.getDeclaredFields().length);
         assertContainsMethod(unionExt3Class, String.class, "getString");
         assertContainsMethod(unionExt3Class, unionExt2Class, "getUnionExt2");
@@ -310,7 +316,8 @@ public class TypedefCompilationTest extends BaseCompilationTest {
         assertContainsField(unionExt4Class, "_int32Ext2", int32Ext2Class);
         assertContainsField(unionExt4Class, "_empty", Boolean.class);
         assertContainsField(unionExt4Class, "_myDecimalType", myDecimalTypeClass);
-        assertContainsFieldWithValue(unionExt4Class, "serialVersionUID", Long.TYPE, 4299836385615211130L, Boolean.class);
+        assertContainsFieldWithValue(unionExt4Class, "serialVersionUID", Long.TYPE, 4299836385615211130L,
+                new Class<?>[] { Boolean.class }, false);
         assertEquals(6, unionExt4Class.getDeclaredFields().length);
         assertContainsMethod(unionExt4Class, unionExt3Class, "getUnionExt3");
         assertContainsMethod(unionExt4Class, int32Ext2Class, "getInt32Ext2");
diff --git a/code-generator/binding-java-api-generator/src/test/resources/compilation/bug1276/foo.yang b/code-generator/binding-java-api-generator/src/test/resources/compilation/bug1276/foo.yang
new file mode 100644 (file)
index 0000000..9f77227
--- /dev/null
@@ -0,0 +1,62 @@
+ module foo {
+
+   namespace "test:yang:union";
+   prefix "foo";
+
+   revision 2014-07-15 {
+   }
+
+
+   typedef ip-address {
+     type union {
+       type ipv4-address;
+       type ipv6-address;
+     }
+   }
+
+   typedef ipv4-address {
+     type string {
+       pattern
+         '(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+       +  '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+       + '(%[\p{N}\p{L}]+)?';
+     }
+   }
+
+   typedef ipv6-address {
+     type string {
+       pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+             + '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+             + '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+             + '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+             + '(%[\p{N}\p{L}]+)?';
+       pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+             + '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+             + '(%.+)?';
+     }
+   }
+
+   typedef domain-name {
+     type string {
+       pattern '((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+            +  '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+            +  '|\.';
+       length "1..253";
+     }
+   }
+
+   typedef host {
+     type union {
+       type ip-address;
+       type domain-name;
+     }
+   }
+
+    typedef int-type {
+        type union {
+            type binary;
+            type int8;
+        }
+    }
+
+ }
index 61b47f2cf5032585b10ce7533692857cd043e282..d59b0215a68cd9e1736b054802ff8a79e9e8dadb 100644 (file)
@@ -7,6 +7,8 @@
  */
 package org.opendaylight.yangtools.yang.parser.builder.impl;
 
+import static org.opendaylight.yangtools.yang.parser.builder.impl.BuilderUtils.findBaseIdentity;
+
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
@@ -86,6 +88,18 @@ public final class TypeUtils {
                 toRemove.add(unionType);
             }
         }
+        // special handling for identityref types under union
+        for (TypeDefinitionBuilder unionType : union.getTypedefs()) {
+            if (unionType instanceof IdentityrefTypeBuilder) {
+                IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) unionType;
+                IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
+                        idref.getLine());
+                if (identity == null) {
+                    throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
+                }
+                idref.setBaseIdentity(identity);
+            }
+        }
         unionTypes.removeAll(toRemove);
     }
 
@@ -93,6 +107,10 @@ public final class TypeUtils {
             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
         final QName utQName = ut.getQName();
         final ModuleBuilder dependentModuleBuilder = BuilderUtils.getModuleByPrefix(module, utQName.getPrefix());
+        if (dependentModuleBuilder == null) {
+            throw new YangParseException(module.getName(), union.getLine(), "No module found with prefix "
+                    + utQName.getPrefix());
+        }
         final TypeDefinitionBuilder resolvedType = findTypeDefinitionBuilder(union, dependentModuleBuilder,
                 utQName.getLocalName(), module.getName(), union.getLine());
         union.setTypedef(resolvedType);