BUG-3051: Fix error reporting string
[mdsal.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / ClassTemplate.xtend
index a9ef53dce08048e93ae3657dace27e378ef448af..f75299436645bde58056f18eaa925a2270751ef9 100644 (file)
@@ -7,24 +7,33 @@
  */
 package org.opendaylight.yangtools.sal.java.api.generator
 
+import com.google.common.base.Preconditions
+import com.google.common.collect.ImmutableList
+import com.google.common.collect.Lists
+import com.google.common.collect.Range
+import com.google.common.io.BaseEncoding
+import java.beans.ConstructorProperties
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.util.ArrayList
+import java.util.Arrays
+import java.util.Collections
 import java.util.List
+import java.util.regex.Pattern
 import org.opendaylight.yangtools.binding.generator.util.TypeConstants
+import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
 import org.opendaylight.yangtools.sal.binding.model.api.Constant
 import org.opendaylight.yangtools.sal.binding.model.api.Enumeration
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import java.util.ArrayList
-import java.util.Collections\rimport java.util.Arrays
 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
-import com.google.common.collect.Range
-import java.util.regex.Pattern
-import com.google.common.io.BaseEncoding
-import java.beans.ConstructorProperties
-import com.google.common.collect.Lists
+import org.opendaylight.yangtools.sal.binding.model.api.Type
+import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
+import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint
 
 /**
- * Template for generating JAVA class. 
+ * Template for generating JAVA class.
  */
 class ClassTemplate extends BaseTemplate {
 
@@ -33,28 +42,29 @@ class ClassTemplate extends BaseTemplate {
     protected val List<GeneratedProperty> parentProperties
     protected val Iterable<GeneratedProperty> allProperties;
     protected val Restrictions restrictions
-    
+
     /**
      * List of enumeration which are generated as JAVA enum type.
      */
     protected val List<Enumeration> enums
-    
+
     /**
      * List of constant instances which are generated as JAVA public static final attributes.
      */
     protected val List<Constant> consts
-    
+
     /**
      * List of generated types which are enclosed inside <code>genType</code>
      */
     protected val List<GeneratedType> enclosedGeneratedTypes;
-    
-    
+
     protected val GeneratedTransferObject genTO;
 
+    private val AbstractRangeGenerator<?> rangeGenerator
+
     /**
      * Creates instance of this class with concrete <code>genType</code>.
-     * 
+     *
      * @param genType generated transfer object which will be transformed to JAVA class source code
      */
     new(GeneratedTransferObject genType) {
@@ -68,37 +78,44 @@ class ClassTemplate extends BaseTemplate {
         var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
         sorted.addAll(properties);
         sorted.addAll(parentProperties);
-        Collections.sort(sorted, new PropertyComparator());
+        Collections.sort(sorted, [p1, p2|
+            p1.name.compareTo(p2.name)
+        ]);
 
         this.allProperties = sorted
         this.enums = genType.enumerations
         this.consts = genType.constantDefinitions
         this.enclosedGeneratedTypes = genType.enclosedTypes
-    }
 
+        if (restrictions != null && !restrictions.rangeConstraints.nullOrEmpty) {
+            rangeGenerator = AbstractRangeGenerator.forType(findProperty(genType, "value").returnType)
+            Preconditions.checkNotNull(rangeGenerator)
+        } else {
+            rangeGenerator = null
+        }
+    }
 
     /**
      * Generates JAVA class source code (class body only).
-     * 
+     *
      * @return string with JAVA class body source code
      */
     def CharSequence generateAsInnerClass() {
         return generateBody(true)
     }
 
-
     override protected body() {
         generateBody(false);
     }
 
     /**
      * Template method which generates class body.
-     * 
+     *
      * @param isInnerClass boolean value which specify if generated class is|isn't inner
      * @return string with class source code in JAVA format
      */
     def protected generateBody(boolean isInnerClass) '''
-        «type.comment.asJavadoc»
+        «wrapToDocumentation(formatDataForJavaDoc(type))»
         «generateClassDeclaration(isInnerClass)» {
             «suidDeclaration»
             «innerClassesDeclarations»
@@ -106,8 +123,17 @@ class ClassTemplate extends BaseTemplate {
             «constantsDeclarations»
             «generateFields»
 
+            «IF restrictions != null»
+                «IF !restrictions.lengthConstraints.nullOrEmpty»
+                    «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraints)»
+                «ENDIF»
+                «IF !restrictions.rangeConstraints.nullOrEmpty»
+                    «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraints)»
+                «ENDIF»
+            «ENDIF»
+
             «constructors»
-            
+
             «defaultInstance»
 
             «FOR field : properties SEPARATOR "\n"»
@@ -117,38 +143,103 @@ class ClassTemplate extends BaseTemplate {
                 «ENDIF»
             «ENDFOR»
 
+            «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
+                «generateGetValueForBitsTypeDef»
+            «ENDIF»
+
             «generateHashCode»
 
             «generateEquals»
 
             «generateToString(genTO.toStringIdentifiers)»
 
-            «generateGetLength»
+            «generateLengthMethod()»
 
-            «generateGetRange»
+            «generateRangeMethod()»
 
         }
+
+    '''
+
+    /**
+     * Template method which generates the method <code>getValue()</code> for typedef,
+     * which base type is BitsDefinition.
+     *
+     * @return string with the <code>getValue()</code> method definition in JAVA format
+     */
+    def protected generateGetValueForBitsTypeDef() '''
+
+        public boolean[] getValue() {
+            return new boolean[]{
+            «FOR property: genTO.properties SEPARATOR ','»
+                 «property.fieldName»
+            «ENDFOR»
+            };
+        }
     '''
 
+    @Deprecated
+    def private generateLengthMethod() '''
+        «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
+            «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
+            /**
+             * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
+             */
+            @Deprecated
+            public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> length() {
+                «List.importedName»<«Range.importedName»<«numberClass.importedName»>> ret = new java.util.ArrayList<>(«restrictions.lengthConstraints.size»);
+                «FOR r : restrictions.lengthConstraints»
+                    ret.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
+                «ENDFOR»
+                return ret;
+            }
+        «ENDIF»
+    '''
+
+    @Deprecated
+    private def rangeBody(List<RangeConstraint> restrictions, Class<? extends Number> numberClass) '''
+        «List.importedName»<«Range.importedName»<«numberClass.importedName»>> ret = new java.util.ArrayList<>(«restrictions.size»);
+        «FOR r : restrictions»
+            ret.add(«Range.importedName».closed(«numericValue(numberClass, r.min)», «numericValue(numberClass, r.max)»));
+        «ENDFOR»
+    '''
+
+    @Deprecated
+    def private generateRangeMethod() '''
+        «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
+            «val returnType = allProperties.iterator.next.returnType»
+            /**
+             * @deprecated This method is slated for removal in a future release. See BUG-1485 for details.
+             */
+            @Deprecated
+            public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> range() {
+            «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
+                «rangeBody(restrictions.rangeConstraints, BigDecimal)»
+            «ELSE»
+                «rangeBody(restrictions.rangeConstraints, BigInteger)»
+            «ENDIF»
+                return ret;
+            }
+        «ENDIF»
+    '''
 
     /**
      * Template method which generates inner classes inside this interface.
-     * 
+     *
      * @return string with the source code for inner classes in JAVA format
      */
     def protected innerClassesDeclarations() '''
         «IF !enclosedGeneratedTypes.empty»
             «FOR innerClass : enclosedGeneratedTypes SEPARATOR "\n"»
                 «IF (innerClass instanceof GeneratedTransferObject)»
-                    «val classTemplate = new ClassTemplate(innerClass as GeneratedTransferObject
+                    «val classTemplate = new ClassTemplate(innerClass)»
                     «classTemplate.generateAsInnerClass»
-                    
+
                 «ENDIF»
             «ENDFOR»
         «ENDIF»
     '''
-    
-    
+
     def protected constructors() '''
         «IF genTO.unionType»
             «genUnionConstructor»
@@ -162,7 +253,7 @@ class ClassTemplate extends BaseTemplate {
             «parentConstructor»
         «ENDIF»
     '''
-    
+
     def protected allValuesConstructor() '''
     «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
         @«ConstructorProperties.importedName»("value")
@@ -171,20 +262,44 @@ class ClassTemplate extends BaseTemplate {
         «IF false == parentProperties.empty»
             super(«parentProperties.asArguments»);
         «ENDIF»
-        «FOR p : allProperties» 
+        «FOR p : allProperties»
             «generateRestrictions(type, p.fieldName.toString, p.returnType)»
         «ENDFOR»
-        «FOR p : properties» 
+
+        «/*
+         * If we have patterns, we need to apply them to the value field. This is a sad
+         * consequence of how this code is structured.
+         */
+        IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
+
+        «Preconditions.importedName».checkNotNull(_value, "Supplied value may not be null");
+
+            «FOR c : consts»
+                «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME && c.value instanceof List<?>»
+                for (Pattern p : patterns) {
+                    «Preconditions.importedName».checkArgument(p.matcher(_value).matches(), "Supplied value \"%s\" does not match required pattern \"%s\"", _value, p);
+                }
+                «ENDIF»
+            «ENDFOR»
+        «ENDIF»
+
+        «FOR p : properties»
+            «IF p.returnType.importedName.contains("[]")»
+            this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
+            «ELSE»
             this.«p.fieldName» = «p.fieldName»;
+            «ENDIF»
         «ENDFOR»
     }
+
     '''
 
     def protected genUnionConstructor() '''
     «FOR p : allProperties»
         «val List<GeneratedProperty> other = new ArrayList(properties)»
-        «val added = other.remove(p)»
-        «genConstructor(p, other)»
+        «IF other.remove(p)»
+            «genConstructor(p, other)»
+        «ENDIF»
     «ENDFOR»
 
     '''
@@ -194,14 +309,40 @@ class ClassTemplate extends BaseTemplate {
         «IF false == parentProperties.empty»
             super(«parentProperties.asArguments»);
         «ENDIF»
-            «generateRestrictions(type, property.fieldName.toString, property.returnType)»
-            this.«property.fieldName» = «property.name»;
-            «FOR p : other»
+
+        «generateRestrictions(type, property.fieldName.toString, property.returnType)»
+
+        this.«property.fieldName» = «property.name»;
+        «FOR p : other»
             this.«p.fieldName» = null;
-            «ENDFOR»
+        «ENDFOR»
     }
     '''
 
+    def private static paramValue(Type returnType, String paramName) {
+        if (returnType instanceof ConcreteType) {
+            return paramName
+        } else {
+            return paramName + ".getValue()"
+        }
+    }
+
+    def private generateRestrictions(Type type, String paramName, Type returnType) '''
+        «val restrictions = type.getRestrictions»
+        «IF restrictions !== null»
+            «IF !restrictions.lengthConstraints.empty || !restrictions.rangeConstraints.empty»
+            if («paramName» != null) {
+                «IF !restrictions.lengthConstraints.empty»
+                    «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
+                «ENDIF»
+                «IF !restrictions.rangeConstraints.empty»
+                    «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
+                «ENDIF»
+                }
+            «ENDIF»
+        «ENDIF»
+    '''
+
     def protected copyConstructor() '''
     /**
      * Creates a copy from Source Object.
@@ -212,7 +353,7 @@ class ClassTemplate extends BaseTemplate {
         «IF false == parentProperties.empty»
             super(source);
         «ENDIF»
-        «FOR p : properties» 
+        «FOR p : properties»
             this.«p.fieldName» = source.«p.fieldName»;
         «ENDFOR»
     }
@@ -235,12 +376,22 @@ class ClassTemplate extends BaseTemplate {
             «IF !("org.opendaylight.yangtools.yang.binding.InstanceIdentifier".equals(prop.returnType.fullyQualifiedName))»
             public static «genTO.name» getDefaultInstance(String defaultValue) {
                 «IF "byte[]".equals(prop.returnType.name)»
-                    «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64(); 
+                    «BaseEncoding.importedName» baseEncoding = «BaseEncoding.importedName».base64();
                     return new «genTO.name»(baseEncoding.decode(defaultValue));
                 «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
                     return new «genTO.name»(defaultValue);
                 «ELSEIF allProperties.size > 1»
                     «bitsArgs»
+                «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(Boolean.valueOf(defaultValue));
+                «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(Byte.valueOf(defaultValue));
+                «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(Short.valueOf(defaultValue));
+                «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(Integer.valueOf(defaultValue));
+                «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(Long.valueOf(defaultValue));
                 «ELSE»
                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
                 «ENDIF»
@@ -257,7 +408,7 @@ class ClassTemplate extends BaseTemplate {
         int i = 0;
         return new «genTO.name»(
         «FOR prop : allProperties SEPARATOR ","»
-            properties.get(i++).equals(defaultValue) ? new «Boolean.importedName»("true") : null
+            properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
         «ENDFOR»
         );
     '''
@@ -270,7 +421,7 @@ class ClassTemplate extends BaseTemplate {
 
     /**
      * Template method which generates JAVA class declaration.
-     * 
+     *
      * @param isInnerClass boolean value which specify if generated class is|isn't inner
      * @return string with class declaration in JAVA format
      */
@@ -293,10 +444,10 @@ class ClassTemplate extends BaseTemplate {
             ENDFOR»«
         ENDIF
     »'''
-    
+
     /**
      * Template method which generates JAVA enum type.
-     * 
+     *
      * @return string with inner enum source code in JAVA format
      */
     def protected enumDeclarations() '''
@@ -310,14 +461,14 @@ class ClassTemplate extends BaseTemplate {
 
     def protected suidDeclaration() '''
         «IF genTO.SUID != null»
-            private static final long serialVersionUID = «genTO.SUID.value»L; 
+            private static final long serialVersionUID = «genTO.SUID.value»L;
         «ENDIF»
     '''
 
     /**
-     * Template method wich generates JAVA constants.
-     * 
-     * @return string with constants in JAVA format 
+     * Template method which generates JAVA constants.
+     *
+     * @return string with constants in JAVA format
      */
     def protected constantsDeclarations() '''
         «IF !consts.empty»
@@ -325,12 +476,11 @@ class ClassTemplate extends BaseTemplate {
                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
                     «val cValue = c.value»
                     «IF cValue instanceof List<?>»
-                        «val cValues = cValue as List<?>»
-                        private static final «List.importedName»<«Pattern.importedName»> «Constants.MEMBER_PATTERN_LIST» = new «ArrayList.importedName»<«Pattern.importedName»>();
-                        public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «Arrays.importedName».asList(«
-                        FOR v : cValues SEPARATOR ", "»«
+                        private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»;
+                        public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
+                        FOR v : cValue SEPARATOR ", "»«
                             IF v instanceof String»"«
-                                v as String»"«
+                                v»"«
                             ENDIF»«
                         ENDFOR»);
 
@@ -350,9 +500,13 @@ class ClassTemplate extends BaseTemplate {
      */
     def protected generateStaticInicializationBlock() '''
         static {
+            final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
+            int i = 0;
             for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
-                «Constants.MEMBER_PATTERN_LIST».add(Pattern.compile(regEx));
+                a[i++] = Pattern.compile(regEx);
             }
+
+            «Constants.MEMBER_PATTERN_LIST» = a;
         }
     '''
 
@@ -364,12 +518,11 @@ class ClassTemplate extends BaseTemplate {
     def protected generateFields() '''
         «IF !properties.empty»
             «FOR f : properties»
-                «IF f.readOnly»final«ENDIF» private «f.returnType.importedName» «f.fieldName»;
+                private«IF f.readOnly» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
             «ENDFOR»
         «ENDIF»
     '''
 
-
     /**
      * Template method which generates the method <code>hashCode()</code>.
      *
@@ -431,30 +584,13 @@ class ClassTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    def private generateGetLength() '''
-        «IF restrictions != null && !(restrictions.lengthConstraints.empty)»
-            «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
-            public static «List.importedName»<«Range.importedName»<«clazz.importedName»>> length() {
-                final «List.importedName»<«Range.importedName»<«clazz.importedName»>> result = new «ArrayList.importedName»<>();
-                «FOR r : restrictions.lengthConstraints»
-                    result.add(«Range.importedName».closed(new «clazz.importedName»("«r.min»"), new «clazz.importedName»("«r.max»")));
-                «ENDFOR»
-                return result;
-            }
-        «ENDIF»
-    '''
-
-    def private generateGetRange() '''
-        «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
-            «val clazz = restrictions.rangeConstraints.iterator.next.min.class»
-            public static «List.importedName»<«Range.importedName»<«clazz.importedName»>> range() {
-                final «List.importedName»<«Range.importedName»<«clazz.importedName»>> result = new «ArrayList.importedName»<>();
-                «FOR r : restrictions.rangeConstraints»
-                    result.add(«Range.importedName».closed(new «clazz.importedName»("«r.min»"), new «clazz.importedName»("«r.max»")));
-                «ENDFOR»
-                return result;
+    def GeneratedProperty getPropByName(String name) {
+        for (GeneratedProperty prop : allProperties) {
+            if (prop.name.equals(name)) {
+                return prop;
             }
-        «ENDIF»
-    '''
+        }
+        return null;
+    }
 
 }