Centralize MoreObjects import
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / ClassTemplate.xtend
index f48ba581ca066b06d84d46beee1e3387bb05c1fd..23be8b693a9bb8937973bbce2d164fbfdcb5bac3 100644 (file)
@@ -8,18 +8,38 @@
 package org.opendaylight.mdsal.binding.java.api.generator
 
 import static java.util.Objects.requireNonNull
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BINARY_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.BOOLEAN_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.EMPTY_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INSTANCE_IDENTIFIER
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT16_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT32_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT64_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.INT8_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.STRING_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT16_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT32_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT64_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BaseYangTypes.UINT8_TYPE
+import static org.opendaylight.mdsal.binding.model.ri.BindingTypes.SCALAR_TYPE_OBJECT
+import static org.opendaylight.mdsal.binding.model.ri.Types.BOOLEAN
+import static org.opendaylight.mdsal.binding.model.ri.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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings
 import java.beans.ConstructorProperties
 import java.util.ArrayList
-import java.util.Collections
+import java.util.Base64;
+import java.util.Collection
+import java.util.Comparator
 import java.util.List
 import java.util.Map
-import java.util.Objects
-import java.util.regex.Pattern
+import java.util.Set
+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
@@ -27,19 +47,32 @@ import org.opendaylight.mdsal.binding.model.api.GeneratedProperty
 import org.opendaylight.mdsal.binding.model.api.GeneratedTransferObject
 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.mdsal.binding.model.ri.TypeConstants
+import org.opendaylight.mdsal.binding.spec.naming.BindingMapping
+import org.opendaylight.yangtools.yang.common.Empty
 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])
+    static val VALUEOF_TYPES = Set.of(
+        BOOLEAN_TYPE,
+        INT8_TYPE,
+        INT16_TYPE,
+        INT32_TYPE,
+        INT64_TYPE,
+        UINT8_TYPE,
+        UINT16_TYPE,
+        UINT32_TYPE,
+        UINT64_TYPE)
 
     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
 
     /**
@@ -78,19 +111,17 @@ class ClassTemplate extends BaseTemplate {
         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
 
         if (restrictions !== null && restrictions.rangeConstraint.present) {
-            rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(findProperty(genType, "value").returnType))
+            rangeGenerator = requireNonNull(AbstractRangeGenerator.forType(TypeUtils.encapsulatedValueType(genType)))
         } else {
             rangeGenerator = null
         }
@@ -116,8 +147,11 @@ class ClassTemplate extends BaseTemplate {
      * @return string with class source code in JAVA format
      */
     def protected generateBody(boolean isInnerClass) '''
-        «wrapToDocumentation(formatDataForJavaDoc(type))»
+        «type.formatDataForJavaDoc.wrapToDocumentation»
         «annotationDeclaration»
+        «IF !isInnerClass»
+            «generatedAnnotation»
+        «ENDIF»
         «generateClassDeclaration(isInnerClass)» {
             «suidDeclaration»
             «innerClassesDeclarations»
@@ -127,7 +161,8 @@ class ClassTemplate extends BaseTemplate {
 
             «IF restrictions !== null»
                 «IF restrictions.lengthConstraint.present»
-                    «LengthGenerator.generateLengthChecker("_value", findProperty(genTO, "value").returnType, restrictions.lengthConstraint.get, this)»
+                    «LengthGenerator.generateLengthChecker("_value", TypeUtils.encapsulatedValueType(genTO),
+                        restrictions.lengthConstraint.get, this)»
                 «ENDIF»
                 «IF restrictions.rangeConstraint.present»
                     «rangeGenerator.generateRangeChecker("_value", restrictions.rangeConstraint.get, this)»
@@ -138,12 +173,7 @@ class ClassTemplate extends BaseTemplate {
 
             «defaultInstance»
 
-            «FOR field : properties SEPARATOR "\n"»
-                «field.getterMethod»
-                «IF !field.readOnly»
-                    «field.setterMethod»
-                «ENDIF»
-            «ENDFOR»
+            «propertyMethods»
 
             «IF (genTO.isTypedef() && genTO.getBaseType instanceof BitsTypeDefinition)»
                 «generateGetValueForBitsTypeDef»
@@ -158,6 +188,38 @@ class ClassTemplate extends BaseTemplate {
 
     '''
 
+    def private propertyMethods() {
+        if (properties.empty) {
+            return ""
+        }
+        isScalarTypeObject ? scalarTypeObjectValue(properties.get(0)) : defaultProperties
+    }
+
+    def private isScalarTypeObject() {
+        for (impl : genTO.implements) {
+            if (SCALAR_TYPE_OBJECT.identifier.equals(impl.identifier)) {
+                return true
+            }
+        }
+        return false
+    }
+
+    def private defaultProperties() '''
+        «FOR field : properties SEPARATOR "\n"»
+            «field.getterMethod»
+            «IF !field.readOnly»
+                «field.setterMethod»
+            «ENDIF»
+        «ENDFOR»
+    '''
+
+    def private scalarTypeObjectValue(GeneratedProperty field) '''
+        @«OVERRIDE.importedName»
+        public «field.returnType.importedName» «BindingMapping.SCALAR_TYPE_OBJECT_GET_VALUE_NAME»() {
+            return «field.fieldName»«field.cloneCall»;
+        }
+    '''
+
     /**
      * Template method which generates the method <code>getValue()</code> for typedef,
      * which base type is BitsDefinition.
@@ -191,9 +253,12 @@ class ClassTemplate extends BaseTemplate {
     def protected constructors() '''
         «IF genTO.unionType»
             «genUnionConstructor»
+        «ELSEIF genTO.typedef && allProperties.size == 1 && allProperties.get(0).name.equals(TypeConstants.VALUE_PROP)»
+            «typedefConstructor»
         «ELSE»
             «allValuesConstructor»
         «ENDIF»
+
         «IF !allProperties.empty»
             «copyConstructor»
         «ENDIF»
@@ -202,41 +267,48 @@ 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 allValuesConstructor() '''
     public «type.name»(«allProperties.asArgumentsDeclaration») {
-        «IF false == parentProperties.empty»
+        «IF !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")»
-            «Objects.importedName».requireNonNull(_value, "Supplied value may not be null");
-            «genPatternEnforcer("_value")»
-        «ENDIF»
-
         «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» == null ? null : «fieldName».clone();
             «ELSE»
-            this.«p.fieldName» = «p.fieldName»;
+                this.«fieldName» = «fieldName»;
             «ENDIF»
         «ENDFOR»
     }
+    '''
+
+    def private typedefConstructor() '''
+    @«ConstructorParameters.importedName»("«TypeConstants.VALUE_PROP»")
+    @«ConstructorProperties.importedName»("«TypeConstants.VALUE_PROP»")
+    public «type.name»(«allProperties.asArgumentsDeclaration») {
+        «IF !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»
+            «val fieldName = p.fieldName»
+            this.«fieldName» = «fieldName»«p.cloneCall»;
+        «ENDFOR»
+    }
     '''
 
     def protected genUnionConstructor() '''
@@ -246,18 +318,18 @@ 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»
+        «IF !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»
@@ -267,7 +339,7 @@ class ClassTemplate extends BaseTemplate {
     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»);
+            «CODEHELPERS.importedName».checkPattern(«ref», «Constants.MEMBER_PATTERN_LIST», «Constants.MEMBER_REGEX_LIST»);
             «ENDIF»
         «ENDFOR»
     '''
@@ -280,7 +352,7 @@ class ClassTemplate extends BaseTemplate {
         }
     }
 
-    def private generateRestrictions(Type type, String paramName, Type returnType) '''
+    def generateRestrictions(Type type, String paramName, Type returnType) '''
         «val restrictions = type.restrictions»
         «IF restrictions !== null»
             «IF restrictions.lengthConstraint.present || restrictions.rangeConstraint.present»
@@ -303,11 +375,12 @@ class ClassTemplate extends BaseTemplate {
      * @param source Source object
      */
     public «type.name»(«type.name» source) {
-        «IF false == parentProperties.empty»
+        «IF !parentProperties.empty»
             super(source);
         «ENDIF»
         «FOR p : properties»
-            this.«p.fieldName» = source.«p.fieldName»;
+            «val fieldName = p.fieldName»
+            this.«fieldName» = source.«fieldName»;
         «ENDFOR»
     }
     '''
@@ -327,42 +400,38 @@ class ClassTemplate extends BaseTemplate {
     def protected defaultInstance() '''
         «IF genTO.typedef && !allProperties.empty && !genTO.unionType»
             «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)»
-                    return new «genTO.name»(defaultValue);
-                «ELSEIF allProperties.size > 1»
+            «val propType = prop.returnType»
+            «IF !(INSTANCE_IDENTIFIER.identifier.equals(propType.identifier))»
+            public static «genTO.name» getDefaultInstance(final String defaultValue) {
+                «IF allProperties.size > 1»
                     «bitsArgs»
-                «ELSEIF "java.lang.Boolean".equals(prop.returnType.fullyQualifiedName)»
-                    return new «genTO.name»(«Boolean.importedName».valueOf(defaultValue));
-                «ELSEIF "java.lang.Byte".equals(prop.returnType.fullyQualifiedName)»
-                    return new «genTO.name»(«Byte.importedName».valueOf(defaultValue));
-                «ELSEIF "java.lang.Short".equals(prop.returnType.fullyQualifiedName)»
-                    return new «genTO.name»(«Short.importedName».valueOf(defaultValue));
-                «ELSEIF "java.lang.Integer".equals(prop.returnType.fullyQualifiedName)»
-                    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 VALUEOF_TYPES.contains(propType)»
+                    return new «genTO.name»(«propType.importedName».valueOf(defaultValue));
+                «ELSEIF STRING_TYPE.equals(propType)»
+                    return new «genTO.name»(defaultValue);
+                «ELSEIF BINARY_TYPE.equals(propType)»
+                    return new «genTO.name»(«Base64.importedName».getDecoder().decode(defaultValue));
+                «ELSEIF EMPTY_TYPE.equals(propType)»
+                    «Preconditions.importedName».checkArgument(defaultValue.isEmpty(), "Invalid value %s", defaultValue);
+                    return new «genTO.name»(«Empty.importedName».value());
                 «ELSE»
-                    return new «genTO.name»(new «prop.returnType.importedName»(defaultValue));
+                    return new «genTO.name»(new «propType.importedName»(defaultValue));
                 «ENDIF»
             }
             «ENDIF»
         «ENDIF»
     '''
 
+    @SuppressFBWarnings(value = "DLS_DEAD_LOCAL_STORE", justification = "FOR with SEPARATOR, not needing for value")
     def protected bitsArgs() '''
-        «List.importedName»<«String.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
+        «JU_LIST.importedName»<«STRING.importedName»> properties = «Lists.importedName».newArrayList(«allProperties.propsAsArgs»);
         if (!properties.contains(defaultValue)) {
             throw new «IllegalArgumentException.importedName»("invalid default parameter");
         }
         int i = 0;
         return new «genTO.name»(
         «FOR prop : allProperties SEPARATOR ","»
-            properties.get(i++).equals(defaultValue) ? «Boolean.importedName».TRUE : null
+            properties.get(i++).equals(defaultValue) ? «BOOLEAN.importedName».TRUE : null
         «ENDFOR»
         );
     '''
@@ -436,13 +505,14 @@ class ClassTemplate extends BaseTemplate {
             «FOR c : consts»
                 «IF c.name == TypeConstants.PATTERN_CONSTANT_NAME»
                     «val cValue = c.value as Map<String, String>»
-                    public static final «List.importedName»<String> «TypeConstants.PATTERN_CONSTANT_NAME» = «ImmutableList.importedName».of(«
+                    «val jurPatternRef = JUR_PATTERN.importedName»
+                    public static final «JU_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.get(0).escapeJava»";
+                        private static final «jurPatternRef» «Constants.MEMBER_PATTERN_LIST» = «jurPatternRef».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 «jurPatternRef»[] «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»
@@ -481,13 +551,17 @@ class ClassTemplate extends BaseTemplate {
             return ""
         }
         return '''
-            @«Override.importedName»
+            @«OVERRIDE.importedName»
             public int hashCode() {
                 «IF size != 1»
-                    «hashCodeResult(genTO.hashCodeIdentifiers)»
+                    final int prime = 31;
+                    int result = 1;
+                    «FOR property : genTO.hashCodeIdentifiers»
+                        result = prime * result + «property.importedUtilClass».hashCode(«property.fieldName»);
+                    «ENDFOR»
                     return result;
                 «ELSE»
-                    return «CodeHelpers.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
+                    return «CODEHELPERS.importedName».wrapperHashCode(«genTO.hashCodeIdentifiers.get(0).fieldName»);
                 «ENDIF»
             }
         '''
@@ -498,20 +572,17 @@ class ClassTemplate extends BaseTemplate {
      *
      * @return string with the <code>equals()</code> method definition in JAVA format
      */
-    def protected generateEquals() '''
+    def private generateEquals() '''
         «IF !genTO.equalsIdentifiers.empty»
-            @«Override.importedName»
-            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.importedUtilClass».equals(«fieldName», other.«fieldName»)) {
@@ -523,13 +594,25 @@ class ClassTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
+    def private generateToString(Collection<? extends GeneratedProperty> properties) '''
+        «IF !properties.empty»
+            @«OVERRIDE.importedName»
+            public «STRING.importedName» toString() {
+                final var helper = «MOREOBJECTS.importedName».toStringHelper(«type.importedName».class);
+                «FOR property : properties»
+                    «CODEHELPERS.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
+                «ENDFOR»
+                return helper.toString();
+            }
+        «ENDIF»
+    '''
+
     def GeneratedProperty getPropByName(String name) {
         for (GeneratedProperty prop : allProperties) {
             if (prop.name.equals(name)) {
                 return prop;
             }
         }
-        return null;
+        return null
     }
-
 }