Document QNAME constant
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / BaseTemplate.xtend
index 5c5f49c07fb5312e77f80860c9fe122597206b19..5d3c27f224730650771a90f5eed3ddc1873e0583 100644 (file)
@@ -7,98 +7,65 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
+import static extension org.opendaylight.mdsal.binding.generator.BindingGeneratorUtil.encodeAngleBrackets
+import static org.opendaylight.mdsal.binding.model.ri.Types.STRING;
+import static org.opendaylight.mdsal.binding.model.ri.Types.objectType;
+
 import com.google.common.base.CharMatcher
 import com.google.common.base.Splitter
-import java.util.Arrays
 import java.util.Collection
-import java.util.HashMap
 import java.util.List
-import java.util.Map
+import java.util.Locale
+import java.util.Map.Entry
 import java.util.StringTokenizer
 import java.util.regex.Pattern
+import org.eclipse.jdt.annotation.NonNull;
+import org.gaul.modernizer_maven_annotations.SuppressModernizer
+import org.opendaylight.mdsal.binding.model.api.AnnotationType
 import org.opendaylight.mdsal.binding.model.api.ConcreteType
 import org.opendaylight.mdsal.binding.model.api.Constant
 import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
-import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
 import org.opendaylight.mdsal.binding.model.api.GeneratedType
+import org.opendaylight.mdsal.binding.model.api.JavaTypeName
 import org.opendaylight.mdsal.binding.model.api.MethodSignature
 import org.opendaylight.mdsal.binding.model.api.Restrictions
 import org.opendaylight.mdsal.binding.model.api.Type
-import org.opendaylight.mdsal.binding.model.api.TypeMember
-import org.opendaylight.mdsal.binding.model.util.Types
-import org.opendaylight.yangtools.yang.common.QName
-
-abstract class BaseTemplate {
-    protected val GeneratedType type;
-    protected val Map<String, String> importMap;
-
-    private static final char NEW_LINE = '\n'
-    private static final CharMatcher NL_MATCHER = CharMatcher.is(NEW_LINE)
-    private static final CharMatcher TAB_MATCHER = CharMatcher.is('\t')
-    private static final Pattern SPACES_PATTERN = Pattern.compile(" +")
-    private static final Splitter NL_SPLITTER = Splitter.on(NL_MATCHER)
-
-    new(GeneratedType _type) {
-        if (_type === null) {
-            throw new IllegalArgumentException("Generated type reference cannot be NULL!")
-        }
-        this.type = _type;
-        this.importMap = new HashMap<String,String>()
+import org.opendaylight.mdsal.binding.model.api.TypeMemberComment
+import org.opendaylight.mdsal.binding.model.ri.TypeConstants
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
+import org.opendaylight.yangtools.yang.binding.BaseIdentity
+
+@SuppressModernizer
+abstract class BaseTemplate extends JavaFileTemplate {
+    static final char NEW_LINE = '\n'
+    static final char SPACE = ' '
+    static val WS_MATCHER = CharMatcher.anyOf("\n\t")
+    static val SPACES_PATTERN = Pattern.compile(" +")
+    static val NL_SPLITTER = Splitter.on(NEW_LINE)
+
+    new(GeneratedType type) {
+        super(type)
     }
 
-    def packageDefinition() '''package «type.packageName»;'''
+    new(AbstractJavaGeneratedType javaType, GeneratedType type) {
+        super(javaType, type)
+    }
 
-    final public def generate() {
+    final def generate() {
         val _body = body()
         '''
-            «packageDefinition»
-            «imports»
+            package «type.packageName»;
+            «generateImportBlock»
 
             «_body»
         '''.toString
     }
 
-    protected def imports() '''
-        «IF !importMap.empty»
-            «FOR entry : importMap.entrySet»
-                «IF !hasSamePackage(entry.value)»
-                    import «entry.value».«entry.key»;
-                «ENDIF»
-            «ENDFOR»
-        «ENDIF»
-
-    '''
-
-    /**
-     * Checks if packages of generated type and imported type is the same
-     *
-     * @param importedTypePackageNam
-     * the package name of imported type
-     * @return true if the packages are the same false otherwise
-     */
-    final private def boolean hasSamePackage(String importedTypePackageName) {
-        return type.packageName.equals(importedTypePackageName);
-    }
-
     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")) {
-            prefix = 2
-        } else if (getter.name.startsWith("get")) {
-            prefix = 3
-        } else {
-            throw new IllegalArgumentException("Not a getter")
-        }
-        return getter.name.substring(prefix).toFirstLower;
-    }
-
-    final protected def isAccessor(MethodSignature maybeGetter) {
-        return maybeGetter.name.startsWith("is") || maybeGetter.name.startsWith("get");
+    final protected def fieldName(GeneratedProperty property) {
+        "_" + property.name
     }
 
     /**
@@ -108,21 +75,20 @@ abstract class BaseTemplate {
      * generated property with data about field which is generated as the getter method
      * @return string with the getter method source code in JAVA format
      */
-    protected def getterMethod(GeneratedProperty field) {
-        '''
-            public «field.returnType.importedName» «field.getterMethodName»() {
-                «IF field.returnType.importedName.contains("[]")»
-                return «field.fieldName» == null ? null : «field.fieldName».clone();
-                «ELSE»
-                return «field.fieldName»;
-                «ENDIF»
-            }
-        '''
-    }
+    protected def getterMethod(GeneratedProperty field) '''
+        «val methodName = field.getterMethodName»
+        public «field.returnType.importedName» «methodName»() {
+            «val fieldName = field.fieldName»
+            «IF field.returnType.name.endsWith("[]")»
+            return «fieldName» == null ? null : «fieldName».clone();
+            «ELSE»
+            return «fieldName»;
+            «ENDIF»
+        }
+    '''
 
     final protected def getterMethodName(GeneratedProperty field) {
-        val prefix = if(field.returnType.equals(Types.BOOLEAN)) "is" else "get"
-        return '''«prefix»«field.name.toFirstUpper»'''
+        return '''«BindingMapping.GETTER_PREFIX»«field.name.toFirstUpper»'''
     }
 
     /**
@@ -140,15 +106,6 @@ abstract class BaseTemplate {
         }
     '''
 
-    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>.
      *
@@ -159,6 +116,17 @@ abstract class BaseTemplate {
     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 method parameters with their types from <code>parameters</code>, annotating them
+     * with {@link NonNull}.
+     *
+     * @param parameters 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 asNonNullArgumentsDeclaration(Iterable<GeneratedProperty> parameters) '''«IF !parameters.empty»
+        «FOR parameter : parameters SEPARATOR ", "»«parameter.returnType.importedNonNull» «parameter
+        .fieldName»«ENDFOR»«ENDIF»'''
+
     /**
      * Template method which generates sequence of the names of the class attributes from <code>parameters</code>.
      *
@@ -166,7 +134,7 @@ 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.
+    def final protected asArguments(Collection<GeneratedProperty> parameters) '''«IF !parameters.empty»«FOR parameter : parameters SEPARATOR ", "»«parameter.
         fieldName»«ENDFOR»«ENDIF»'''
 
     /**
@@ -175,30 +143,28 @@ abstract class BaseTemplate {
      * @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 ''
-        var txt = comment
+    def final protected asJavadoc(TypeMemberComment comment) {
+        if (comment === null) {
+            return ''
+        }
+        return wrapToDocumentation('''
+           «comment.contractDescription»
 
-        txt = comment.trim
-        txt = formatToParagraph(txt)
+           «comment.referenceDescription.formatReference»
 
-        return '''
-            «wrapToDocumentation(txt)»
-        '''
+           «comment.typeSignature»
+        ''')
     }
 
-    def String wrapToDocumentation(String text) {
+    def static String wrapToDocumentation(String text) {
         if (text.empty)
             return ""
 
-        val StringBuilder sb = new StringBuilder("/**")
-        sb.append(NEW_LINE)
-
+        val StringBuilder sb = new StringBuilder().append("/**\n")
         for (String t : NL_SPLITTER.split(text)) {
             sb.append(" *")
             if (!t.isEmpty()) {
-                sb.append(' ');
-                sb.append(t)
+                sb.append(SPACE).append(t)
             }
             sb.append(NEW_LINE)
         }
@@ -208,68 +174,57 @@ abstract class BaseTemplate {
     }
 
     def protected String formatDataForJavaDoc(GeneratedType type) {
-        val typeDescription = type.getDescription().encodeJavadocSymbols;
+        val sb = new StringBuilder()
+        val comment = type.comment
+        if (comment !== null) {
+            sb.append(comment.javadoc)
+        }
+
+        appendSnippet(sb, type)
 
         return '''
-            «IF !typeDescription.nullOrEmpty»
-            «typeDescription»
+            «IF sb.length != 0»
+            «sb.toString»
             «ENDIF»
         '''.toString
     }
 
-    private static final CharMatcher AMP_MATCHER = CharMatcher.is('&');
-
-    def encodeJavadocSymbols(String description) {
-        if (description.nullOrEmpty) {
-            return description;
+    def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
+        val comment = type.comment
+        if (comment === null) {
+            return '''
+                «additionalComment»
+            '''
         }
 
-        var ret = description.replace("*/", "&#42;&#47;");
-        ret = AMP_MATCHER.replaceFrom(ret, "&amp;");
+        val sb = new StringBuilder().append(comment.javadoc)
+        appendSnippet(sb, type)
 
-        return ret;
-    }
-
-    def protected String formatDataForJavaDoc(GeneratedType type, String additionalComment) {
-        val StringBuilder typeDescription = new StringBuilder();
-        if (!type.description.nullOrEmpty) {
-            typeDescription.append(type.description)
-            typeDescription.append(NEW_LINE)
-            typeDescription.append(NEW_LINE)
-            typeDescription.append(NEW_LINE)
-            typeDescription.append(additionalComment)
-        } else {
-            typeDescription.append(additionalComment)
-        }
+        sb.append(NEW_LINE)
+        .append(NEW_LINE)
+        .append(NEW_LINE)
+        .append(additionalComment)
 
         return '''
-            «typeDescription.toString»
-        '''.toString
+            «sb.toString»
+        '''
     }
 
-    def protected String formatDataForJavaDoc(TypeMember type, String additionalComment) {
-        val StringBuilder typeDescriptionBuilder = new StringBuilder();
-        if (!type.comment.nullOrEmpty) {
-            typeDescriptionBuilder.append(formatToParagraph(type.comment))
-            typeDescriptionBuilder.append(NEW_LINE)
-            typeDescriptionBuilder.append(NEW_LINE)
-            typeDescriptionBuilder.append(NEW_LINE)
-        }
-        typeDescriptionBuilder.append(additionalComment)
-        var typeDescription = wrapToDocumentation(typeDescriptionBuilder.toString)
-        return '''
-            «typeDescription»
-        '''.toString
-    }
+    def static formatReference(String reference) '''
+        «IF reference !== null»
+            <pre>
+                <code>
+                    «reference.encodeAngleBrackets.formatToParagraph»
+                </code>
+            </pre>
 
-    def asCode(String text) {
-        return "<code>" + text + "</code>"
-    }
+        «ENDIF»
+    '''
 
     def asLink(String text) {
         val StringBuilder sb = new StringBuilder()
         var tempText = text
-        var char lastChar = ' '
+        var char lastChar = SPACE
         var boolean badEnding = false
 
         if (text.endsWith('.') || text.endsWith(':') || text.endsWith(',')) {
@@ -277,105 +232,49 @@ abstract class BaseTemplate {
             lastChar = text.charAt(text.length - 1)
             badEnding = true
         }
-        sb.append("<a href = \"")
-        sb.append(tempText)
-        sb.append("\">")
-        sb.append(tempText)
-        sb.append("</a>")
+        sb.append("<a href = \"").append(tempText).append("\">").append(tempText).append("</a>")
 
-        if(badEnding)
+        if (badEnding)
             sb.append(lastChar)
 
         return sb.toString
     }
 
-    protected def formatToParagraph(String text) {
-        if(text === null || text.isEmpty)
-            return text
-
-        var formattedText = text
+    protected static def formatToParagraph(String inputText) {
         val StringBuilder sb = new StringBuilder();
         var StringBuilder lineBuilder = new StringBuilder();
         var boolean isFirstElementOnNewLineEmptyChar = false;
 
-        formattedText = encodeJavadocSymbols(formattedText)
-        formattedText = NL_MATCHER.removeFrom(formattedText)
-        formattedText = TAB_MATCHER.removeFrom(formattedText)
+        var formattedText = WS_MATCHER.replaceFrom(inputText.encodeJavadocSymbols, SPACE)
         formattedText = SPACES_PATTERN.matcher(formattedText).replaceAll(" ")
 
-        val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true);
-
-        while (tokenizer.hasMoreElements) {
-            val nextElement = tokenizer.nextElement.toString
+        val StringTokenizer tokenizer = new StringTokenizer(formattedText, " ", true)
+        while (tokenizer.hasMoreTokens) {
+            val nextElement = tokenizer.nextToken
 
             if (lineBuilder.length != 0 && 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(lineBuilder.length - 1) == SPACE) {
+                    lineBuilder.setLength(lineBuilder.length - 1)
                 }
-                if (lineBuilder.charAt(0) == ' ') {
-                    lineBuilder.setLength(0)
-                    lineBuilder.append(lineBuilder.substring(1))
+                if (lineBuilder.length != 0 && lineBuilder.charAt(0) == SPACE) {
+                    lineBuilder.deleteCharAt(0)
                 }
 
-                sb.append(lineBuilder);
+                sb.append(lineBuilder).append(NEW_LINE)
                 lineBuilder.setLength(0)
-                sb.append(NEW_LINE)
 
-                if(nextElement.toString == ' ') {
+                if (nextElement == " ") {
                     isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar;
                 }
             }
-
             if (isFirstElementOnNewLineEmptyChar) {
                 isFirstElementOnNewLineEmptyChar = !isFirstElementOnNewLineEmptyChar
-            }
-
-            else {
+            } else {
                 lineBuilder.append(nextElement)
             }
         }
-        sb.append(lineBuilder)
-        sb.append(NEW_LINE)
-
-        return sb.toString
-    }
-
-    def protected generateToString(Collection<GeneratedProperty> properties) '''
-        «IF !properties.empty»
-            @Override
-            public «String.importedName» toString() {
-                «StringBuilder.importedName» builder = new «StringBuilder.importedName»(«type.importedName».class.getSimpleName()).append(" [");
-                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 getRestrictions(Type type) {
-        var Restrictions restrictions = null
-        if (type instanceof ConcreteType) {
-            restrictions = type.restrictions
-        } else if (type instanceof GeneratedTransferObject) {
-            restrictions = type.restrictions
-        }
-        return restrictions
+        return sb.append(lineBuilder).append(NEW_LINE).toString
     }
 
     /**
@@ -393,27 +292,98 @@ abstract class BaseTemplate {
         ENDIF
     »'''
 
-    def protected GeneratedProperty findProperty(GeneratedTransferObject gto, String name) {
-        val props = gto.properties
-        for (prop : props) {
-            if (prop.name.equals(name)) {
-                return prop
-            }
-        }
-        val GeneratedTransferObject parent = gto.superType
-        if (parent !== null) {
-            return findProperty(parent, name)
-        }
-        return null
-    }
-
     def protected emitConstant(Constant c) '''
-        «IF c.value instanceof QName»
-            «val qname = c.value as QName»
-            public static final «c.type.importedName» «c.name» = «QName.name».create("«qname.namespace.toString»",
-                "«qname.formattedRevision»", "«qname.localName»").intern();
+        «IF BindingMapping.QNAME_STATIC_FIELD_NAME.equals(c.name)»
+            «val entry = c.value as Entry<JavaTypeName, String>»
+            /**
+             * YANG identifier of the statement represented by this class.
+             */
+            public static final «c.type.importedNonNull» «c.name» = «entry.key.importedName».«BindingMapping.MODULE_INFO_QNAMEOF_METHOD_NAME»("«entry.value»");
+        «ELSEIF BindingMapping.VALUE_STATIC_FIELD_NAME.equals(c.name) && BaseIdentity.equals(c.value)»
+            «val typeName = c.type.importedName»
+            «val override = OVERRIDE.importedName»
+            /**
+             * Singleton value representing the {@link «typeName»} identity.
+             */
+            public static final «c.type.importedNonNull» «c.name» = new «typeName»() {
+                @«override»
+                public «CLASS.importedName»<«typeName»> «BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»() {
+                    return «typeName».class;
+                }
+
+                @«override»
+                public int hashCode() {
+                    return «typeName».class.hashCode();
+                }
+
+                @«override»
+                public boolean equals(final «objectType.importedName» obj) {
+                    return obj == this || obj instanceof «typeName»
+                        && «typeName».class.equals(((«typeName») obj).«BindingMapping.BINDING_CONTRACT_IMPLEMENTED_INTERFACE_NAME»());
+                }
+
+                @«override»
+                public «STRING.importedName» toString() {
+                    return «MOREOBJECTS.importedName».toStringHelper("«c.type.name»").add("qname", QNAME).toString();
+                }
+            };
         «ELSE»
             public static final «c.type.importedName» «c.name» = «c.value»;
         «ENDIF»
     '''
+
+    def protected generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
+       «IF restrictions.rangeConstraint.present»
+           «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
+               restrictions.rangeConstraint.get, this)»
+       «ENDIF»
+       «IF restrictions.lengthConstraint.present»
+           «LengthGenerator.generateLengthChecker(field.fieldName, actualType, restrictions.lengthConstraint.get, this)»
+       «ENDIF»
+    '''
+
+    def protected checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType, String value) '''
+       «IF restrictions.getRangeConstraint.isPresent»
+           «IF actualType instanceof ConcreteType»
+               «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value)»
+           «ELSE»
+               «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, value + ".getValue()")»
+           «ENDIF»
+       «ENDIF»
+       «val fieldName = property.fieldName»
+       «IF restrictions.getLengthConstraint.isPresent»
+           «IF actualType instanceof ConcreteType»
+               «LengthGenerator.generateLengthCheckerCall(fieldName, value)»
+           «ELSE»
+               «LengthGenerator.generateLengthCheckerCall(fieldName, value + ".getValue()")»
+           «ENDIF»
+       «ENDIF»
+
+       «val fieldUpperCase = fieldName.toUpperCase(Locale.ENGLISH)»
+       «FOR currentConstant : type.getConstantDefinitions»
+           «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
+               && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
+           «CODEHELPERS.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«fieldName», «Constants.MEMBER_REGEX_LIST»«fieldName»);
+           «ENDIF»
+       «ENDFOR»
+    '''
+
+    def protected hashCodeResult(Collection<? extends GeneratedProperty> properties) '''
+        final int prime = 31;
+        int result = 1;
+        «FOR property : properties»
+            result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
+        «ENDFOR»
+    '''
+
+    def protected final generateAnnotation(AnnotationType annotation) '''
+        @«annotation.importedName»
+        «IF annotation.parameters !== null && !annotation.parameters.empty»
+        (
+        «FOR param : annotation.parameters SEPARATOR ","»
+            «param.name»=«param.value»
+        «ENDFOR»
+        )
+        «ENDIF»
+    '''
 }