Add JavaTypeNames for annotation types
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / ClassTemplate.xtend
index f403720f944029ac834342bae7180d8501406e62..6c330ef91b482389d22a03160f4755943708ecc0 100644 (file)
@@ -7,37 +7,51 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
+import static java.util.Objects.requireNonNull
+import static org.opendaylight.mdsal.binding.model.util.Types.BOOLEAN;
+import static org.opendaylight.mdsal.binding.model.util.Types.BYTE_ARRAY;
+import static org.opendaylight.mdsal.binding.model.util.Types.STRING;
+import static extension org.apache.commons.text.StringEscapeUtils.escapeJava
+
 import com.google.common.base.Preconditions
 import com.google.common.collect.ImmutableList
 import com.google.common.collect.Lists
-import com.google.common.io.BaseEncoding
 import java.beans.ConstructorProperties
 import java.util.ArrayList
-import java.util.Arrays
-import java.util.Collections
+import java.util.Base64;
+import java.util.Comparator
 import java.util.List
-import java.util.Objects
+import java.util.Map
 import java.util.regex.Pattern
-import org.opendaylight.mdsal.binding.generator.util.TypeConstants
+import javax.management.ConstructorParameters
+import org.gaul.modernizer_maven_annotations.SuppressModernizer
 import org.opendaylight.mdsal.binding.model.api.ConcreteType
 import org.opendaylight.mdsal.binding.model.api.Constant
 import org.opendaylight.mdsal.binding.model.api.Enumeration
 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.Restrictions
 import org.opendaylight.mdsal.binding.model.api.Type
+import org.opendaylight.mdsal.binding.model.util.TypeConstants
+import org.opendaylight.yangtools.yang.binding.CodeHelpers
+import org.opendaylight.yangtools.yang.common.Empty
+import org.opendaylight.yangtools.yang.common.Uint16
+import org.opendaylight.yangtools.yang.common.Uint32
+import org.opendaylight.yangtools.yang.common.Uint64
+import org.opendaylight.yangtools.yang.common.Uint8
 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition
 
 /**
  * Template for generating JAVA class.
  */
+@SuppressModernizer
 class ClassTemplate extends BaseTemplate {
+    static val Comparator<GeneratedProperty> PROP_COMPARATOR = Comparator.comparing([prop | prop.name])
 
     protected val List<GeneratedProperty> properties
     protected val List<GeneratedProperty> finalProperties
     protected val List<GeneratedProperty> parentProperties
-    protected val Iterable<GeneratedProperty> allProperties;
+    protected val List<GeneratedProperty> allProperties
     protected val Restrictions restrictions
 
     /**
@@ -50,14 +64,9 @@ class ClassTemplate extends BaseTemplate {
      */
     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
 
-    protected val GeneratedTransferObject genTO;
-
-    private val AbstractRangeGenerator<?> rangeGenerator
+    val AbstractRangeGenerator<?> rangeGenerator
 
     /**
      * Creates instance of this class with concrete <code>genType</code>.
@@ -65,28 +74,33 @@ class ClassTemplate extends BaseTemplate {
      * @param genType generated transfer object which will be transformed to JAVA class source code
      */
     new(GeneratedTransferObject genType) {
-        super(genType)
+        this(new TopLevelJavaGeneratedType(genType), genType)
+    }
+
+    /**
+     * 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(AbstractJavaGeneratedType javaType, GeneratedTransferObject genType) {
+        super(javaType, genType)
         this.genTO = genType
         this.properties = genType.properties
         this.finalProperties = GeneratorUtil.resolveReadOnlyPropertiesFromTO(genTO.properties)
         this.parentProperties = GeneratorUtil.getPropertiesOfAllParents(genTO)
         this.restrictions = genType.restrictions
 
-        var List<GeneratedProperty> sorted = new ArrayList<GeneratedProperty>();
+        val sorted = new ArrayList();
         sorted.addAll(properties);
         sorted.addAll(parentProperties);
-        Collections.sort(sorted, [p1, p2|
-            p1.name.compareTo(p2.name)
-        ]);
+        sorted.sort(PROP_COMPARATOR);
 
         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)
+        if (restrictions !== null && restrictions.rangeConstraint.present) {
+            rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
         } else {
             rangeGenerator = null
         }
@@ -122,11 +136,11 @@ class ClassTemplate extends BaseTemplate {
             «generateFields»
 
             «IF restrictions !== null»
-                «IF !restrictions.lengthConstraints.nullOrEmpty»
-                    «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraints)»
+                «IF restrictions.lengthConstraint.present»
+                    «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
                 «ENDIF»
-                «IF !restrictions.rangeConstraints.nullOrEmpty»
-                    «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraints)»
+                «IF restrictions.rangeConstraint.present»
+                    «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
                 «ENDIF»
             «ENDIF»
 
@@ -177,13 +191,9 @@ class ClassTemplate extends BaseTemplate {
      * @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)»
-                    «classTemplate.generateAsInnerClass»
-
-                «ENDIF»
+        «IF !type.enclosedTypes.empty»
+            «FOR innerClass : type.enclosedTypes SEPARATOR "\n"»
+                «generateInnerClass(innerClass)»
             «ENDFOR»
         «ENDIF»
     '''
@@ -191,9 +201,14 @@ class ClassTemplate extends BaseTemplate {
     def protected constructors() '''
         «IF genTO.unionType»
             «genUnionConstructor»
+        «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
+            «typedefConstructor»
+            «legacyConstructor»
         «ELSE»
             «allValuesConstructor»
+            «legacyConstructor»
         «ENDIF»
+
         «IF !allProperties.empty»
             «copyConstructor»
         «ENDIF»
@@ -202,51 +217,77 @@ class ClassTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    def protected allValuesConstructor() '''
-    «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name.equals("value")»
-        @«ConstructorProperties.importedName»("value")
-    «ENDIF»
+    def private allValuesConstructor() '''
     public «type.name»(«allProperties.asArgumentsDeclaration») {
         «IF false == parentProperties.empty»
             super(«parentProperties.asArguments»);
         «ENDIF»
         «FOR p : allProperties»
-            «generateRestrictions(type, p.fieldName.toString, p.returnType)»
+            «generateRestrictions(type, p.fieldName, p.returnType)»
         «ENDFOR»
 
-        «/*
-         * 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 p : properties»
+            «val fieldName = p.fieldName»
+            «IF p.returnType.name.endsWith("[]")»
+                this.«fieldName» = «fieldName» == null ? null : «fieldName».clone();
+            «ELSE»
+                this.«fieldName» = «fieldName»;
+            «ENDIF»
+        «ENDFOR»
+    }
+    '''
 
-            «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»
+    def private typedefConstructor() '''
+    @«ConstructorParameters.importedName»("value")
+    @«ConstructorProperties.importedName»("value")
+    public «type.name»(«allProperties.asArgumentsDeclaration») {
+        «IF false == parentProperties.empty»
+            super(«parentProperties.asArguments»);
         «ENDIF»
+        «FOR p : allProperties»
+            «generateRestrictions(type, p.fieldName, p.returnType)»
+        «ENDFOR»
+        «/*
+         * If we have patterns, we need to apply them to the value field. This is a sad consequence of how this code is
+         * structured.
+         */»
+        «CodeHelpers.importedName».requireValue(_value);
+        «genPatternEnforcer("_value")»
 
         «FOR p : properties»
-            «IF p.returnType.importedName.contains("[]")»
-                «IF genTO.typedef && !allProperties.empty && allProperties.size == 1 && allProperties.get(0).name
-                .equals("value")»
-                this.«p.fieldName» = «p.fieldName».clone();
-                «ELSE»
-                this.«p.fieldName» = «p.fieldName» == null ? null : «p.fieldName».clone();
-                «ENDIF»
+            «val fieldName = p.fieldName»
+            «IF p.returnType.name.endsWith("[]")»
+                this.«fieldName» = «fieldName».clone();
             «ELSE»
-            this.«p.fieldName» = «p.fieldName»;
+                this.«fieldName» = «fieldName»;
             «ENDIF»
         «ENDFOR»
     }
-
     '''
 
+    def private legacyConstructor() {
+        if (!hasUintProperties) {
+            return ""
+        }
+
+        val compatUint = CodeHelpers.importedName + ".compatUint("
+        return '''
+
+            /**
+             * Utility migration constructor.
+             *
+             «FOR prop : allProperties»
+             * @param «prop.fieldName» «prop.name»«IF prop.isUintType» in legacy Java type«ENDIF»
+             «ENDFOR»
+             * @deprecated Use {#link «type.name»(«FOR prop : allProperties SEPARATOR ", "»«prop.returnType.importedJavadocName»«ENDFOR»)} instead.
+             */
+            @Deprecated(forRemoval = true)
+            public «type.getName»(«FOR prop : allProperties SEPARATOR ", "»«prop.legacyType.importedName» «prop.fieldName»«ENDFOR») {
+                this(«FOR prop : allProperties SEPARATOR ", "»«IF prop.isUintType»«compatUint»«prop.fieldName»)«ELSE»«prop.fieldName»«ENDIF»«ENDFOR»);
+            }
+        '''
+    }
+
     def protected genUnionConstructor() '''
     «FOR p : allProperties»
         «val List<GeneratedProperty> other = new ArrayList(properties)»
@@ -254,24 +295,32 @@ class ClassTemplate extends BaseTemplate {
             «genConstructor(p, other)»
         «ENDIF»
     «ENDFOR»
-
     '''
 
-    def protected genConstructor(GeneratedProperty property, GeneratedProperty... other) '''
+    def protected genConstructor(GeneratedProperty property, Iterable<GeneratedProperty> other) '''
     public «type.name»(«property.returnType.importedName + " " + property.name») {
         «IF false == parentProperties.empty»
             super(«parentProperties.asArguments»);
         «ENDIF»
 
-        «generateRestrictions(type, property.fieldName.toString, property.returnType)»
+        «val fieldName = property.fieldName»
+        «generateRestrictions(type, fieldName, property.returnType)»
 
-        this.«property.fieldName» = «property.name»;
+        this.«fieldName» = «property.name»;
         «FOR p : other»
             this.«p.fieldName» = null;
         «ENDFOR»
     }
     '''
 
+    def private genPatternEnforcer(String ref) '''
+        «FOR c : consts»
+            «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
+            «CodeHelpers.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
+            «ENDIF»
+        «ENDFOR»
+    '''
+
     def private static paramValue(Type returnType, String paramName) {
         if (returnType instanceof ConcreteType) {
             return paramName
@@ -281,17 +330,17 @@ class ClassTemplate extends BaseTemplate {
     }
 
     def private generateRestrictions(Type type, String paramName, Type returnType) '''
-        «val restrictions = type.getRestrictions»
+        «val restrictions = type.restrictions»
         «IF restrictions !== null»
-            «IF !restrictions.lengthConstraints.empty || !restrictions.rangeConstraints.empty»
+            «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
             if («paramName» != null) {
-                «IF !restrictions.lengthConstraints.empty»
+                «IF restrictions.lengthConstraint.present»
                     «LengthGenerator.generateLengthCheckerCall(paramName, paramValue(returnType, paramName))»
                 «ENDIF»
-                «IF !restrictions.rangeConstraints.empty»
+                «IF restrictions.rangeConstraint.present»
                     «rangeGenerator.generateRangeCheckerCall(paramName, paramValue(returnType, paramName))»
                 «ENDIF»
-                }
+            }
             «ENDIF»
         «ENDIF»
     '''
@@ -307,7 +356,8 @@ class ClassTemplate extends BaseTemplate {
             super(source);
         «ENDIF»
         «FOR p : properties»
-            this.«p.fieldName» = source.«p.fieldName»;
+            «val fieldName = p.fieldName»
+            this.«fieldName» = source.«fieldName»;
         «ENDFOR»
     }
     '''
@@ -319,7 +369,8 @@ class ClassTemplate extends BaseTemplate {
      * @param source Source object
      */
     public «type.name»(«genTO.superType.importedName» source) {
-            super(source);
+        super(source);
+        «genPatternEnforcer("getValue()")»
     }
     '''
 
@@ -328,14 +379,16 @@ class ClassTemplate extends BaseTemplate {
             «val prop = allProperties.get(0)»
             «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();
-                    return new «genTO.name»(baseEncoding.decode(defaultValue));
-                «ELSEIF "java.lang.String".equals(prop.returnType.fullyQualifiedName)»
+                «IF BYTE_ARRAY.equals(prop.returnType)»
+                    return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
+                «ELSEIF STRING.equals(prop.returnType)»
                     return new «genTO.name»(defaultValue);
+                «ELSEIF Constants.EMPTY.equals(prop.returnType)»
+                    «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
+                    return new «genTO.name»(«Empty.importedName».getInstance());
                 «ELSEIF allProperties.size > 1»
                     «bitsArgs»
-                «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
+                «ELSEIF BOOLEAN.equals(prop.returnType)»
                     return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
                 «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
                     return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
@@ -345,6 +398,14 @@ class ClassTemplate extends BaseTemplate {
                     return new «genTO.name»(«Integer.importedName».valueOf(defaultValue));
                 «ELSEIF "java.lang.Long".equals(prop.returnType.fullyQualifiedName)»
                     return new «genTO.name»(«Long.importedName».valueOf(defaultValue));
+                «ELSEIF "org.opendaylight.yangtools.yang.common.Uint8".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(«Uint8.importedName».valueOf(defaultValue));
+                «ELSEIF "org.opendaylight.yangtools.yang.common.Uint16".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(«Uint16.importedName».valueOf(defaultValue));
+                «ELSEIF "org.opendaylight.yangtools.yang.common.Uint32".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(«Uint32.importedName».valueOf(defaultValue));
+                «ELSEIF "org.opendaylight.yangtools.yang.common.Uint64".equals(prop.returnType.fullyQualifiedName)»
+                    return new «genTO.name»(«Uint64.importedName».valueOf(defaultValue));
                 «ELSE»
                     return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
                 «ENDIF»
@@ -406,8 +467,7 @@ class ClassTemplate extends BaseTemplate {
     def protected enumDeclarations() '''
         «IF !enums.empty»
             «FOR e : enums SEPARATOR "\n"»
-                «val enumTemplate = new EnumTemplate(e)»
-                «enumTemplate.generateAsInnerClass»
+                «new EnumTemplate(javaType.getEnclosedType(e.identifier), e).generateAsInnerClass»
             «ENDFOR»
         «ENDIF»
     '''
@@ -435,17 +495,16 @@ class ClassTemplate extends BaseTemplate {
         «IF !consts.empty»
             «FOR c : consts»
                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
-                    «val cValue = c.value»
-                    «IF cValue instanceof List<?>»
-                        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»"«
-                            ENDIF»«
-                        ENDFOR»);
-
-                        «generateStaticInitializationBlock»
+                    «val cValue = c.value as Map<String, String>»
+                    public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
+                    FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
+                    «IF cValue.size == 1»
+                        private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST» = «Pattern.importedName».compile(«TypeConstants.PATTERN_CONSTANT_NAME».get(0));
+                        private static final String «Constants.MEMBER_REGEX_LIST» = "«cValue.values.iterator.next.escapeJava»";
+                    «ELSE»
+                        private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST» = «CodeHelpers.importedName».compilePatterns(«TypeConstants.PATTERN_CONSTANT_NAME»);
+                        private static final String[] «Constants.MEMBER_REGEX_LIST» = { «
+                        FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
                     «ENDIF»
                 «ELSE»
                     «emitConstant(c)»
@@ -454,23 +513,6 @@ class ClassTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    /**
-     * Template method which generates JAVA static initialization block.
-     *
-     * @return string with static initialization block in JAVA format
-     */
-    def protected generateStaticInitializationBlock() '''
-        static {
-            final «Pattern.importedName» a[] = new «Pattern.importedName»[«TypeConstants.PATTERN_CONSTANT_NAME».size()];
-            int i = 0;
-            for (String regEx : «TypeConstants.PATTERN_CONSTANT_NAME») {
-                a[i++] = Pattern.compile(regEx);
-            }
-
-            «Constants.MEMBER_PATTERN_LIST» = a;
-        }
-    '''
-
     /**
      * Template method which generates JAVA class attributes.
      *
@@ -493,50 +535,43 @@ class ClassTemplate extends BaseTemplate {
      *
      * @return string with the <code>hashCode()</code> method definition in JAVA format
      */
-    def protected generateHashCode() '''
-        «IF !genTO.hashCodeIdentifiers.empty»
-            @Override
+    def protected generateHashCode() {
+        val size = genTO.hashCodeIdentifiers.size
+        if (size == 0) {
+            return ""
+        }
+        return '''
+            @«OVERRIDE.importedName»
             public int hashCode() {
-                final int prime = 31;
-                int result = 1;
-                «FOR property : genTO.hashCodeIdentifiers»
-                    «IF property.returnType.name.contains("[")»
-                    result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
-                    «ELSE»
-                    result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
-                    «ENDIF»
-                «ENDFOR»
-                return result;
+                «IF size != 1»
+                    «hashCodeResult(genTO.hashCodeIdentifiers)»
+                    return result;
+                «ELSE»
+                    return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
+                «ENDIF»
             }
-        «ENDIF»
-    '''
+        '''
+    }
 
     /**
      * Template method which generates the method <code>equals()</code>.
      *
      * @return string with the <code>equals()</code> method definition in JAVA format
      */
-    def protected generateEquals() '''
+    def private generateEquals() '''
         «IF !genTO.equalsIdentifiers.empty»
-            @Override
-            public boolean equals(java.lang.Object obj) {
+            @«OVERRIDE.importedName»
+            public final boolean equals(java.lang.Object obj) {
                 if (this == obj) {
                     return true;
                 }
-                if (obj == null) {
-                    return false;
-                }
-                if (getClass() != obj.getClass()) {
+                if (!(obj instanceof «type.name»)) {
                     return false;
                 }
-                «type.name» other = («type.name») obj;
+                final «type.name» other = («type.name») obj;
                 «FOR property : genTO.equalsIdentifiers»
                     «val fieldName = property.fieldName»
-                    «IF property.returnType.name.contains("[")»
-                    if (!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
-                    «ELSE»
-                    if (!«Objects.importedName».equals(«fieldName», other.«fieldName»)) {
-                    «ENDIF»
+                    if (!«property.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
                         return false;
                     }
                 «ENDFOR»
@@ -551,7 +586,28 @@ class ClassTemplate extends BaseTemplate {
                 return prop;
             }
         }
-        return null;
+        return null
     }
 
+    def private hasUintProperties() {
+        for (GeneratedProperty prop : allProperties) {
+            if (prop.isUintType) {
+                return true
+            }
+        }
+        return false
+    }
+
+    def private static isUintType(GeneratedProperty prop) {
+        UINT_TYPES.containsKey(prop.returnType)
+    }
+
+    def private static legacyType(GeneratedProperty prop) {
+        val type = prop.returnType
+        val uint = UINT_TYPES.get(type)
+        if (uint !== null) {
+            return uint
+        }
+        return type
+    }
 }