Bug 1672 - Generated javadoc contains symbols of javadoc comment tags
[mdsal.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BaseTemplate.xtend
index 672d4b648e94eea593dbd1d04a8d6415bcefa751..26c95d2fa3236738d775797a41816f4c458adaf8 100644 (file)
@@ -7,29 +7,30 @@
  */
 package org.opendaylight.yangtools.sal.java.api.generator
 
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
-import java.util.Map
-import org.opendaylight.yangtools.sal.binding.model.api.Type
-import org.opendaylight.yangtools.binding.generator.util.Types
-import com.google.common.base.Splitter
-import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
+import com.google.common.collect.ImmutableList
 import com.google.common.collect.Range
+import java.math.BigDecimal
+import java.math.BigInteger
+import java.util.Arrays
+import java.util.Collection
+import java.util.HashMap
 import java.util.List
+import java.util.Map
+import java.util.StringTokenizer
+import org.opendaylight.yangtools.binding.generator.util.Types
 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.GeneratedProperty
 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
-import java.util.Collection
-import java.util.Arrays
-import java.util.HashMap
-import com.google.common.collect.ImmutableList
-import java.math.BigInteger
-import java.math.BigDecimal
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
+import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
+import org.opendaylight.yangtools.sal.binding.model.api.Restrictions
+import org.opendaylight.yangtools.sal.binding.model.api.Type
 
 abstract class BaseTemplate {
     protected val GeneratedType type;
     protected val Map<String, String> importMap;
-    static val paragraphSplitter = Splitter.on("\n\n").omitEmptyStrings();
+
+    private static final String NEW_LINE = '\n'
 
     new(GeneratedType _type) {
         if (_type == null) {
@@ -50,7 +51,7 @@ abstract class BaseTemplate {
         '''
             «packageDefinition»
             «imports»
-            
+
             «_body»
         '''.toString
     }
@@ -63,7 +64,7 @@ abstract class BaseTemplate {
                 «ENDIF»
             «ENDFOR»
         «ENDIF»
-        
+
     '''
 
     protected abstract def CharSequence body();
@@ -154,22 +155,142 @@ abstract class BaseTemplate {
      * @return string with comment in JAVA format
      */
     def protected CharSequence asJavadoc(String comment) {
-        if(comment == null) return '';
+        if(comment == null) return ''
         var txt = comment
-        if (txt.contains("*/")) {
-            txt = txt.replace("*/", "&#42;&#47;")
-        }
-        val paragraphs = paragraphSplitter.split(txt)
+
+        txt = comment.trim
+        txt = formatToParagraph(txt)
 
         return '''
-            /**
-              «FOR p : paragraphs SEPARATOR "<p>"»
-                  «p»
-              «ENDFOR»
-            **/
+            «wrapToDocumentation(txt)»
         '''
     }
 
+    def String wrapToDocumentation(String text) {
+        val StringTokenizer tokenizer = new StringTokenizer(text, "\n", false)
+        val StringBuilder sb = new StringBuilder()
+
+        if(text.empty)
+            return ""
+
+        sb.append("/**")
+        sb.append(NEW_LINE)
+
+        while(tokenizer.hasMoreTokens) {
+            sb.append(" * ")
+            sb.append(tokenizer.nextToken)
+            sb.append(NEW_LINE)
+        }
+        sb.append(" */")
+
+        return sb.toString
+    }
+
+    def protected String formatDataForJavaDoc(GeneratedType type) {
+        val typeDescription = type.getDescription().encodeJavadocSymbols;
+
+        return '''
+            «IF !typeDescription.nullOrEmpty»
+            «typeDescription»
+            «ENDIF»
+        '''.toString
+    }
+
+    def encodeJavadocSymbols(String description) {
+        if (!description.nullOrEmpty) {
+            return description.replace("*/", "&#42;&#47;")
+        }
+        return description;
+    }
+
+    def asLink(String text) {
+        val StringBuilder sb = new StringBuilder()
+        var tempText = text
+        var char lastChar = ' '
+        var boolean badEnding = false
+
+        if(text.endsWith(".") || text.endsWith(":") || text.endsWith(",")) {
+            tempText = text.substring(0, text.length - 1)
+            lastChar = text.charAt(text.length - 1)
+            badEnding = true
+        }
+        sb.append("<a href = \"")
+        sb.append(tempText)
+        sb.append("\">")
+        sb.append(tempText)
+        sb.append("</a>")
+
+        if(badEnding)
+            sb.append(lastChar)
+
+        return sb.toString
+    }
+
+    protected def formatToParagraph(String text) {
+        if(text == null || text.isEmpty)
+            return text
+
+        var formattedText = text
+        val StringBuilder sb = new StringBuilder();
+        var StringBuilder lineBuilder = new StringBuilder();
+        var boolean isFirstElementOnNewLineEmptyChar = false;
+
+        formattedText = formattedText.encodeJavadocSymbols
+        formattedText = formattedText.replace(NEW_LINE, "")
+        formattedText = formattedText.replace("\t", "")
+        formattedText = formattedText.replaceAll(" +", " ");
+
+        val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
+
+        while(tokenizer.hasMoreElements) {
+            val nextElement = tokenizer.nextElement.toString
+
+            if(lineBuilder.length + nextElement.length > 80) {
+                if (lineBuilder.charAt(lineBuilder.length - 1) == ' ') {
+                    lineBuilder.setLength(0)
+                    lineBuilder.append(lineBuilder.substring(0, lineBuilder.length - 1))
+                }
+                if (lineBuilder.charAt(0) == ' ') {
+                    lineBuilder.setLength(0)
+                    lineBuilder.append(lineBuilder.substring(1))
+                }
+
+                sb.append(lineBuilder);
+                lineBuilder.setLength(0)
+                sb.append(NEW_LINE)
+
+                if(nextElement.toString == ' ') {
+                    isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
+                }
+            }
+
+            if(isFirstElementOnNewLineEmptyChar) {
+                isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
+            }
+
+            else {
+                lineBuilder.append(nextElement)
+            }
+        }
+        sb.append(lineBuilder)
+        sb.append(NEW_LINE)
+
+        return sb.toString
+    }
+
+    def isDocumentationParametersNullOrEmtpy(GeneratedType type) {
+        val boolean isTypeDescriptionNullOrEmpty = type.description.nullOrEmpty
+        val boolean isTypeReferenceNullOrEmpty = type.reference.nullOrEmpty
+        val boolean isTypeModuleNameNullOrEmpty = type.moduleName.nullOrEmpty
+        val boolean isTypeSchemaPathNullOrEmpty = type.schemaPath.nullOrEmpty
+
+        if (isTypeDescriptionNullOrEmpty && isTypeReferenceNullOrEmpty && isTypeModuleNameNullOrEmpty
+            && isTypeSchemaPathNullOrEmpty) {
+            return true
+        }
+        return false
+    }
+
     def generateRestrictions(Type type, String paramName, Type returnType) '''
         «val restrictions = type.getRestrictions»
         «IF restrictions !== null»
@@ -178,7 +299,7 @@ abstract class BaseTemplate {
                 «generateLengthRestriction(returnType, restrictions, paramName, isNestedType)»
             «ENDIF»
             «IF !restrictions.rangeConstraints.empty»
-                «generateRangeRestriction(returnType, restrictions, paramName, isNestedType)»
+                «generateRangeRestriction(returnType, paramName, isNestedType)»
             «ENDIF»
         «ENDIF»
     '''
@@ -199,12 +320,11 @@ abstract class BaseTemplate {
         }
     '''
 
-    def private generateRangeRestriction(Type returnType, Restrictions restrictions, String paramName, boolean isNestedType) '''
-        «val clazz = restrictions.rangeConstraints.iterator.next.min.class»
+    def private generateRangeRestriction(Type returnType, String paramName, boolean isNestedType) '''
         if («paramName» != null) {
-            «printRangeConstraint(returnType, clazz, paramName, isNestedType)»
+            «printRangeConstraint(returnType, paramName, isNestedType)»
             boolean isValidRange = false;
-            for («Range.importedName»<«clazz.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»range()) {
+            for («Range.importedName»<«returnType.importedNumber»> r : «IF isNestedType»«returnType.importedName».«ENDIF»range()) {
                 if (r.contains(_constraint)) {
                     isValidRange = true;
                 }
@@ -223,22 +343,22 @@ abstract class BaseTemplate {
         «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»«IF isNestedType».getValue()«ENDIF».length«IF !isArray»()«ENDIF»);
     '''
 
-    def printRangeConstraint(Type returnType, Class<? extends Number> clazz, String paramName, boolean isNestedType) '''
-        «IF clazz.canonicalName.equals(BigDecimal.canonicalName)»
-            «clazz.importedNumber» _constraint = new «clazz.importedNumber»(«paramName»«IF isNestedType».getValue()«ENDIF».toString());
+    def printRangeConstraint(Type returnType, String paramName, boolean isNestedType) '''
+        «IF BigDecimal.canonicalName.equals(returnType.fullyQualifiedName)»
+            «BigDecimal.importedName» _constraint = new «BigDecimal.importedName»(«paramName»«IF isNestedType».getValue()«ENDIF».toString());
         «ELSE»
             «IF isNestedType»
                 «val propReturnType = findProperty(returnType as GeneratedTransferObject, "value").returnType»
                 «IF propReturnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
-                    «clazz.importedNumber» _constraint = «paramName».getValue();
+                    «BigInteger.importedName» _constraint = «paramName».getValue();
                 «ELSE»
-                    «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName».getValue());
+                    «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName».getValue());
                 «ENDIF»
             «ELSE»
                 «IF returnType.fullyQualifiedName.equals(BigInteger.canonicalName)»
-                    «clazz.importedNumber» _constraint = «paramName»;
+                    «BigInteger.importedName» _constraint = «paramName»;
                 «ELSE»
-                    «clazz.importedNumber» _constraint = «clazz.importedNumber».valueOf(«paramName»);
+                    «BigInteger.importedName» _constraint = «BigInteger.importedName».valueOf(«paramName»);
                 «ENDIF»
             «ENDIF»
         «ENDIF»
@@ -248,7 +368,7 @@ abstract class BaseTemplate {
         «IF !properties.empty»
             @Override
             public «String.importedName» toString() {
-                «StringBuilder.importedName» builder = new «StringBuilder.importedName»("«type.name» [");
+                «StringBuilder.importedName» builder = new «StringBuilder.importedName»(«type.importedName».class.getSimpleName()).append(" [");
                 boolean first = true;
 
                 «FOR property : properties»
@@ -271,15 +391,6 @@ abstract class BaseTemplate {
         «ENDIF»
     '''
 
-    def GeneratedProperty getPropByName(GeneratedType gt, String name) {
-        for (GeneratedProperty prop : gt.properties) {
-            if (prop.name.equals(name)) {
-                return prop;
-            }
-        }
-        return null;
-    }
-
     def getRestrictions(Type type) {
         var Restrictions restrictions = null
         if (type instanceof ConcreteType) {
@@ -324,15 +435,15 @@ abstract class BaseTemplate {
             «val numberClass = restrictions.lengthConstraints.iterator.next.min.class»
             public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
                 «IF numberClass.equals(typeof(BigDecimal))»
-                    «lengthMethodBody(restrictions, numberClass, className, varName)»
+                    «lengthBody(restrictions, numberClass, className, varName)»
                 «ELSE»
-                    «lengthMethodBody(restrictions, typeof(BigInteger), className, varName)»
+                    «lengthBody(restrictions, typeof(BigInteger), className, varName)»
                 «ENDIF»
             }
         «ENDIF»
     '''
 
-    def private lengthMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def private lengthBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -347,21 +458,33 @@ abstract class BaseTemplate {
         return «varName»;
     '''
 
-    def protected generateRangeMethod(String methodName, Type type, String className, String varName) '''
-        «val Restrictions restrictions = type.restrictions»
+    def protected generateRangeMethod(String methodName, Restrictions restrictions, Type returnType, String className, String varName) '''
         «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
-            «val numberClass = restrictions.rangeConstraints.iterator.next.min.class»
-            public static «List.importedName»<«Range.importedName»<«numberClass.importedNumber»>> «methodName»() {
-                «IF numberClass.equals(typeof(BigDecimal)
-                    «rangeMethodBody(restrictions, numberClass, className, varName)»
+            «val number = returnType.importedNumber»
+            public static «List.importedName»<«Range.importedName»<«number»>> «methodName»() {
+                «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName
+                    «rangeBody(restrictions, BigDecimal, className, varName)»
                 «ELSE»
-                    «rangeMethodBody(restrictions, typeof(BigInteger), className, varName)»
+                    «rangeBody(restrictions, BigInteger, className, varName)»
                 «ENDIF»
             }
         «ENDIF»
     '''
 
-    def private rangeMethodBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
+    def protected generateRangeMethod(String methodName, Restrictions restrictions, String className, String varName, Iterable<GeneratedProperty> properties) '''
+        «IF restrictions != null && !(restrictions.rangeConstraints.empty)»
+            «val returnType = properties.iterator.next.returnType»
+            public static «List.importedName»<«Range.importedName»<«returnType.importedNumber»>> «methodName»() {
+                «IF returnType.fullyQualifiedName.equals(BigDecimal.canonicalName)»
+                    «rangeBody(restrictions, BigDecimal, className, varName)»
+                «ELSE»
+                    «rangeBody(restrictions, BigInteger, className, varName)»
+                «ENDIF»
+            }
+        «ENDIF»
+    '''
+
+    def private rangeBody(Restrictions restrictions, Class<? extends Number> numberClass, String className, String varName) '''
         if («varName» == null) {
             synchronized («className».class) {
                 if («varName» == null) {
@@ -383,7 +506,14 @@ abstract class BaseTemplate {
         return BigInteger.importedName
     }
 
-    def private String numericValue(Class<? extends Number> clazz, Object numberValue) {
+    def protected String importedNumber(Type clazz) {
+        if (clazz.fullyQualifiedName.equals(BigDecimal.canonicalName)) {
+            return BigDecimal.importedName
+        }
+        return BigInteger.importedName
+    }
+
+    def protected String numericValue(Class<? extends Number> clazz, Object numberValue) {
         val number = clazz.importedName;
         val value = numberValue.toString
         if (clazz.equals(typeof(BigInteger)) || clazz.equals(typeof(BigDecimal))) {