Bug 1193: Use format string correctly and fix corresponding unit test
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BaseTemplate.xtend
index 532850f37bc10ce9c8ffde1a8c7b8c8e4faf3a2e..fc9535e0d2bd84386d3fd8545be3511bb22b6f06 100644 (file)
@@ -1,3 +1,10 @@
+/*
+ * 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.sal.java.api.generator
 
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
@@ -14,38 +21,39 @@ import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
 import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
 import java.util.Collection
+import java.util.Arrays
 
 abstract class BaseTemplate {
-    
-    
+
     protected val GeneratedType type;
-    protected val Map<String,String> importMap;
+    protected val Map<String, String> importMap;
     static val paragraphSplitter = Splitter.on("\n\n").omitEmptyStrings();
+
     new(GeneratedType _type) {
-         if (_type== null) {
+        if (_type == null) {
             throw new IllegalArgumentException("Generated type reference cannot be NULL!")
         }
         this.type = _type;
         this.importMap = GeneratorUtil.createImports(type)
     }
-    
-    def packageDefinition () '''package «type.packageName»;'''
 
+    def packageDefinition() '''package «type.packageName»;'''
 
     protected def getFullyQualifiedName() {
         return type.fullyQualifiedName
     }
-    
-    final public def generate() {
-    val _body = body()
-    '''
-    «packageDefinition»
-    «imports»
 
-    «_body»
-    '''.toString
+    final public def generate() {
+        val _body = body()
+        '''
+            «packageDefinition»
+            «imports»
+            
+            «_body»
+        '''.toString
     }
-    protected def imports()  ''' 
+
+    protected def imports() ''' 
         «IF !importMap.empty»
             «FOR entry : importMap.entrySet»
                 «IF entry.value != fullyQualifiedName»
@@ -53,16 +61,14 @@ abstract class BaseTemplate {
                 «ENDIF»
             «ENDFOR»
         «ENDIF»
-
+        
     '''
 
     protected abstract def CharSequence body();
 
     // Helper patterns
-
     final protected def fieldName(GeneratedProperty property) '''_«property.name»'''
 
-
     final protected def propertyNameFromGetter(MethodSignature getter) {
         var int prefix;
         if (getter.name.startsWith("is")) {
@@ -83,18 +89,18 @@ abstract class BaseTemplate {
      * @return string with the getter method source code in JAVA format 
      */
     final protected def getterMethod(GeneratedProperty field) {
-    '''
-        public «field.returnType.importedName» «field.getterMethodName»() {
-            return «field.fieldName»;
-        }
-    '''
+        '''
+            public «field.returnType.importedName» «field.getterMethodName»() {
+                return «field.fieldName»;
+            }
+        '''
     }
 
     final protected def getterMethodName(GeneratedProperty field) {
         val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
         return '''«prefix»«field.name.toFirstUpper»'''
     }
-    
+
     /**
      * Template method which generates the setter method for <code>field</code>
      * 
@@ -109,16 +115,16 @@ abstract class BaseTemplate {
             return this;
         }
     '''
-    
+
     final protected def importedName(Type intype) {
         GeneratorUtil.putTypeIntoImports(type, intype, importMap);
         GeneratorUtil.getExplicitType(type, intype, importMap)
     }
-    
+
     final protected def importedName(Class<?> cls) {
         importedName(Types.typeForClass(cls))
     }
-    
+
     /**
      * Template method which generates method parameters with their types from <code>parameters</code>.
      * 
@@ -126,9 +132,9 @@ abstract class BaseTemplate {
      * group of generated property instances which are transformed to the method parameters
      * @return string with the list of the method parameters with their types in JAVA format
      */
-    def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) 
-    '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
-    
+    def final protected asArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
+        returnType.importedName» «parameter.fieldName»«ENDFOR»«ENDIF»'''
+
     /**
      * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
      * 
@@ -136,67 +142,68 @@ abstract class BaseTemplate {
      * group of generated property instances which are transformed to the sequence of parameter names
      * @return string with the list of the parameter names of the <code>parameters</code> 
      */
-    def final protected asArguments(Iterable<GeneratedProperty> parameters) 
-    '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.fieldName»«ENDFOR»«ENDIF»'''
-    
-    
-        /**
+    def final protected asArguments(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
+        fieldName»«ENDFOR»«ENDIF»'''
+
+    /**
      * Template method which generates JAVA comments.
      * 
      * @param comment string with the comment for whole JAVA class
      * @return string with comment in JAVA format
      */
     def protected CharSequence asJavadoc(String comment) {
-        if (comment==null) return '';
-        val paragraphs = paragraphSplitter.split(comment)
-        
+        if(comment == null) return '';
+        var txt = comment
+        if (txt.contains("*/")) {
+            txt = txt.replace("*/", "&#42;&#47;")
+        }
+        val paragraphs = paragraphSplitter.split(txt)
+
         return '''
             /**
               «FOR p : paragraphs SEPARATOR "<p>"»
-              «p»
+                  «p»
               «ENDFOR»
             **/
-            '''
+        '''
     }
 
-    def generateLengthRestrictions(Type type, String paramName, Type returnType) '''
+    def generateRestrictions(Type type, String paramName, Type returnType) '''
         «val boolean isArray = returnType.name.contains("[")»
-        «IF type instanceof ConcreteType»
-            «val restrictions = (type as ConcreteType).restrictions»
-            «IF restrictions !== null && !restrictions.lengthConstraints.empty»
-                «generateLengthRestriction(type, restrictions, paramName, isArray, !(returnType instanceof ConcreteType))»
-            «ENDIF»
-        «ENDIF»
-        «IF type instanceof GeneratedTransferObject»
-            «val restrictions = (type as GeneratedTransferObject).restrictions»
-            «IF restrictions !== null && !restrictions.lengthConstraints.empty»
-                «generateLengthRestriction(type, restrictions, paramName, isArray, !(returnType instanceof ConcreteType))»
-            «ENDIF»
-        «ENDIF»
+        «processRestrictions(type, paramName, returnType, isArray)»
     '''
 
-    def generateLengthRestrictions(GeneratedProperty field, String paramName) '''
+    def generateRestrictions(GeneratedProperty field, String paramName) '''
         «val Type type = field.returnType»
         «IF type instanceof ConcreteType»
-            «val boolean isArray = type.name.contains("[")»
-            «val restrictions = (type as ConcreteType).restrictions»
-            «IF restrictions !== null && !restrictions.lengthConstraints.empty»
-                «generateLengthRestriction(type, restrictions, paramName, isArray, false)»
-            «ENDIF»
+            «processRestrictions(type, paramName, field.returnType, type.name.contains("["))»
+        «ELSEIF type instanceof GeneratedTransferObject»
+            «processRestrictions(type, paramName, field.returnType, isArrayType(type as GeneratedTransferObject))»
         «ENDIF»
-        «IF type instanceof GeneratedTransferObject»
-            «var isArray = isArrayType(type as GeneratedTransferObject)»
-            «val restrictions = (type as GeneratedTransferObject).restrictions»
-            «IF restrictions !== null && !restrictions.lengthConstraints.empty»
-                «generateLengthRestriction(type, restrictions, paramName, isArray, true)»
+    '''
+
+
+    private def processRestrictions(Type type, String paramName, Type returnType, boolean isArray) '''
+        «val restrictions = type.getRestrictions»
+        «IF restrictions !== null»
+            «IF !restrictions.lengthConstraints.empty»
+                «generateLengthRestriction(type, restrictions, paramName, isArray,
+            !(returnType instanceof ConcreteType))»
+            «ENDIF»
+            «IF !restrictions.rangeConstraints.empty &&
+            ("java.lang".equals(returnType.packageName) || "java.math".equals(returnType.packageName))»
+                «generateRangeRestriction(type, returnType, restrictions, paramName,
+            !(returnType instanceof ConcreteType))»
             «ENDIF»
         «ENDIF»
     '''
 
-    def generateLengthRestriction(Type type, Restrictions restrictions, String paramName, boolean isArray, boolean isNestedType) '''
+    def generateLengthRestriction(Type type, Restrictions restrictions, String paramName, boolean isArray,
+        boolean isNestedType) '''
         if («paramName» != null) {
             boolean isValidLength = false;
-            «List.importedName»<«Range.importedName»<«Integer.importedName»>> lengthConstraints = new «ArrayList.importedName»<>(); 
+            «List.importedName»<«Range.importedName»<«Integer.importedName»>> lengthConstraints = new «ArrayList.
+            importedName»<>(); 
             «FOR r : restrictions.lengthConstraints»
                 lengthConstraints.add(«Range.importedName».closed(«r.min», «r.max»));
             «ENDFOR»
@@ -214,15 +221,68 @@ abstract class BaseTemplate {
                         if (r.contains(«paramName».length())) {
                     «ENDIF»
                 «ENDIF»
-                    isValidLength = true;
+                isValidLength = true;
                 }
             }
             if (!isValidLength) {
-                throw new IllegalArgumentException("illegal length");
+                throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», lengthConstraints));
+            }
+        }
+    '''
+
+    def generateRangeRestriction(Type type, Type returnType, Restrictions restrictions, String paramName,
+        boolean isNestedType) '''
+        «val javaType = Class.forName(returnType.fullyQualifiedName)»
+        if («paramName» != null) {
+            boolean isValidRange = false;
+            «List.importedName»<«Range.importedName»<«javaType.importedName»>> rangeConstraints = new «ArrayList.
+            importedName»<>(); 
+            «FOR r : restrictions.rangeConstraints»
+                rangeConstraints.add(«Range.importedName».closed(new «javaType.importedName»(«r.min.toQuote»), new «javaType.
+            importedName»(«r.max.toQuote»)));
+            «ENDFOR»
+            for («Range.importedName»<«javaType.importedName»> r : rangeConstraints) {
+                «IF isNestedType»
+                    if (r.contains(«paramName».getValue())) {
+                «ELSE»
+                    if (r.contains(«paramName»)) {
+                «ENDIF»
+                isValidRange = true;
+                }
+            }
+            if (!isValidRange) {
+                throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», rangeConstraints));
             }
         }
     '''
 
+    def protected generateToString(Collection<GeneratedProperty> properties) '''
+        «IF !properties.empty»
+            @Override
+            public String toString() {
+                StringBuilder builder = new StringBuilder("«type.name» [");
+                boolean first = true;
+
+                «FOR property : properties»
+                    if («property.fieldName» != null) {
+                        if (first) {
+                            first = false;
+                        } else {
+                            builder.append(", ");
+                        }
+                        builder.append("«property.fieldName»=");
+                        «IF property.returnType.name.contains("[")»
+                            builder.append(«Arrays.importedName».toString(«property.fieldName»));
+                        «ELSE»
+                            builder.append(«property.fieldName»);
+                        «ENDIF»
+                     }
+                «ENDFOR»
+                return builder.append(']').toString();
+            }
+        «ENDIF»
+    '''
+
     def GeneratedProperty getPropByName(GeneratedType gt, String name) {
         for (GeneratedProperty prop : gt.properties) {
             if (prop.name.equals(name)) {
@@ -232,13 +292,14 @@ abstract class BaseTemplate {
         return null;
     }
 
-    def GeneratedProperty getPropByName(Collection<GeneratedProperty> props, String name) {
-        for (GeneratedProperty prop : props) {
-            if (prop.name.equals(name)) {
-                return prop;
-            }
+    def getRestrictions(Type type) {
+        var Restrictions restrictions = null
+        if (type instanceof ConcreteType) {
+            restrictions = (type as ConcreteType).restrictions
+        } else if (type instanceof GeneratedTransferObject) {
+            restrictions = (type as GeneratedTransferObject).restrictions
         }
-        return null;
+        return restrictions
     }
 
     def boolean isArrayType(GeneratedTransferObject type) {
@@ -261,4 +322,23 @@ abstract class BaseTemplate {
         return base;
     }
 
+    def String toQuote(Object obj) {
+        return "\"" + obj.toString + "\"";
+    }
+
+    /**
+     * Template method which generates method parameters with their types from <code>parameters</code>.
+     * 
+     * @param parameters
+     * list of parameter instances which are transformed to the method parameters
+     * @return string with the list of the method parameters with their types in JAVA format
+     */
+    def protected generateParameters(List<MethodSignature.Parameter> parameters) '''«
+        IF !parameters.empty»«
+            FOR parameter : parameters SEPARATOR ", "»«
+                parameter.type.importedName» «parameter.name»«
+            ENDFOR»«
+        ENDIF
+    »'''
+
 }