Rework Java import tracking
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / BuilderTemplate.xtend
index 877f26542b5485c59f08deba77bb29a85fcc89bf..fb79ec096f7611d1474ec6af68b994a99ba2e1a4 100644 (file)
@@ -7,7 +7,12 @@
  */
 package org.opendaylight.mdsal.binding.java.api.generator
 
+import static extension org.apache.commons.text.StringEscapeUtils.escapeJava;
+
+import com.google.common.base.MoreObjects
+import com.google.common.collect.ImmutableMap
 import com.google.common.collect.ImmutableSortedSet
+import com.google.common.collect.ImmutableList
 import java.util.ArrayList
 import java.util.Arrays
 import java.util.Collection
@@ -19,18 +24,24 @@ import java.util.List
 import java.util.Map
 import java.util.Objects
 import java.util.Set
+import java.util.regex.Pattern
 import org.opendaylight.mdsal.binding.model.api.ConcreteType
 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.Type
+import org.opendaylight.mdsal.binding.model.api.ParameterizedType
+import org.opendaylight.mdsal.binding.model.api.Restrictions
 import org.opendaylight.mdsal.binding.model.util.ReferencedTypeImpl
 import org.opendaylight.mdsal.binding.model.util.Types
-import org.opendaylight.mdsal.binding.model.util.generated.type.builder.GeneratedTOBuilderImpl
+import org.opendaylight.mdsal.binding.model.util.generated.type.builder.CodegenGeneratedTOBuilder
+import org.opendaylight.mdsal.binding.model.util.TypeConstants
 import org.opendaylight.yangtools.concepts.Builder
 import org.opendaylight.yangtools.yang.binding.Augmentable
 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
+import org.opendaylight.yangtools.yang.binding.CodeHelpers
 import org.opendaylight.yangtools.yang.binding.DataObject
 import org.opendaylight.yangtools.yang.binding.Identifiable
 
@@ -77,9 +88,14 @@ class BuilderTemplate extends BaseTemplate {
      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
      */
     new(GeneratedType genType) {
-        super(genType)
+        super(new TopLevelJavaGeneratedType(builderName(genType), genType), genType)
         this.properties = propertiesFromMethods(createMethods)
-        importMap.put(Builder.simpleName, Builder.package.name)
+        addImport(Builder)
+    }
+
+    def static builderName(GeneratedType genType) {
+        val name = genType.identifier
+        name.createSibling(name.simpleName + "Builder")
     }
 
     /**
@@ -116,15 +132,13 @@ class BuilderTemplate extends BaseTemplate {
             } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
                 for (m : Augmentable.methods) {
                     if (m.name == GET_AUGMENTATION_METHOD_NAME) {
-                        val fullyQualifiedName = m.returnType.name
-                        val pkg = fullyQualifiedName.package
-                        val name = fullyQualifiedName.name
-                        val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
-                        val refType = new ReferencedTypeImpl(pkg, name)
-                        val generic = new ReferencedTypeImpl(type.packageName, type.name)
+                        val identifier = JavaTypeName.create(m.returnType)
+                        val tmpGenTO = new CodegenGeneratedTOBuilder(identifier)
+                        val refType = new ReferencedTypeImpl(identifier)
+                        val generic = new ReferencedTypeImpl(type.identifier)
                         val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
                         tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
-                        augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
+                        augmentField = tmpGenTO.build.methodDefinitions.first.propertyFromGetter
                     }
                 }
             }
@@ -140,28 +154,6 @@ class BuilderTemplate extends BaseTemplate {
         elements.get(0)
     }
 
-    /**
-     * Returns the name of the package from <code>fullyQualifiedName</code>.
-     *
-     * @param fullyQualifiedName string with fully qualified type name (package + type)
-     * @return string with the package name
-     */
-    def private String getPackage(String fullyQualifiedName) {
-        val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
-        return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
-    }
-
-    /**
-     * Returns the name of tye type from <code>fullyQualifiedName</code>
-     *
-     * @param fullyQualifiedName string with fully qualified type name (package + type)
-     * @return string with the name of the type
-     */
-    def private String getName(String fullyQualifiedName) {
-        val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
-        return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
-    }
-
     /**
      * Creates set of generated property instances from getter <code>methods</code>.
      *
@@ -204,13 +196,13 @@ class BuilderTemplate extends BaseTemplate {
         }
         if (method.name.startsWith(prefix)) {
             val fieldName = method.getName().substring(prefix.length()).toFirstLower
-            val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
+            val tmpGenTO = new CodegenGeneratedTOBuilder(JavaTypeName.create("foo", "foo"))
             tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
-            return tmpGenTO.toInstance.properties.first
+            return tmpGenTO.build.properties.first
         }
     }
 
-    override isLocalInnerClass(String importedTypePackageName) {
+    override isLocalInnerClass(JavaTypeName name) {
         // Builders do not have inner types
         return false;
     }
@@ -226,6 +218,8 @@ class BuilderTemplate extends BaseTemplate {
 
             «generateFields(false)»
 
+            «constantsDeclarations()»
+
             «generateAugmentField(false)»
 
             «generateConstructorsFromIfcs(type)»
@@ -321,12 +315,7 @@ class BuilderTemplate extends BaseTemplate {
                     «FOR impl : ifc.getAllIfcs»
                         «generateIfCheck(impl, done)»
                     «ENDFOR»
-                    if (!isValidArg) {
-                        throw new IllegalArgumentException(
-                          "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
-                          "but was: " + arg
-                        );
-                    }
+                    «CodeHelpers.importedName».validValue(isValidArg, arg, "«ifc.getAllIfcs.toListOfNames»");
                 }
             «ENDIF»
         «ENDIF»
@@ -334,7 +323,7 @@ class BuilderTemplate extends BaseTemplate {
 
     def private generateMethodFieldsFromComment(GeneratedType type) '''
         /**
-         *Set fields from given grouping argument. Valid argument is instance of one of following types:
+         * Set fields from given grouping argument. Valid argument is instance of one of following types:
          * <ul>
          «FOR impl : type.getAllIfcs»
          * <li>«impl.fullyQualifiedName»</li>
@@ -430,54 +419,124 @@ class BuilderTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
+    def private constantsDeclarations() '''
+        «FOR c : type.getConstantDefinitions»
+            «IF c.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)»
+                «val cValue = c.value as Map<String, String>»
+                «val String fieldSuffix = c.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length)»
+                «IF cValue.size == 1»
+                   private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile("«cValue.keySet.get(0).escapeJava»");
+                   private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
+                «ELSE»
+                   private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«ImmutableList.importedName».of(
+                   «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»));
+                   private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
+                   FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
+                «ENDIF»
+            «ELSE»
+                «emitConstant(c)»
+            «ENDIF»
+        «ENDFOR»
+    '''
+
+    def private 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.toString, actualType, restrictions.lengthConstraint.get, this)»
+       «ENDIF»
+    '''
+
+    def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
+       «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»
+       «IF restrictions.getLengthConstraint.isPresent»
+           «IF actualType instanceof ConcreteType»
+               «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
+           «ELSE»
+               «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
+           «ENDIF»
+       «ENDIF»
+
+       «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
+       «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»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
+           «ENDIF»
+       «ENDFOR»
+    '''
+
+    def private Restrictions restrictionsForSetter(Type actualType) {
+        if (actualType instanceof GeneratedType) {
+            return null;
+        }
+        return actualType.restrictions;
+    }
+
+    def private generateListSetter(GeneratedProperty field, Type actualType) '''
+        «val restrictions = restrictionsForSetter(actualType)»
+        «IF restrictions !== null»
+            «generateCheckers(field, restrictions, actualType)»
+        «ENDIF»
+        public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
+        «IF restrictions !== null»
+            if (values != null) {
+               for («actualType.getFullyQualifiedName» value : values) {
+                   «checkArgument(field, restrictions, actualType)»
+               }
+            }
+        «ENDIF»
+            this.«field.fieldName.toString» = values;
+            return this;
+        }
+
+    '''
+
+    def private generateSetter(GeneratedProperty field, Type actualType) '''
+        «val restrictions = restrictionsForSetter(actualType)»
+        «IF restrictions !== null»
+            «generateCheckers(field, restrictions, actualType)»
+        «ENDIF»
+
+        public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
+        «IF restrictions !== null»
+            if (value != null) {
+                «checkArgument(field, restrictions, actualType)»
+            }
+        «ENDIF»
+            this.«field.fieldName.toString» = value;
+            return this;
+        }
+    '''
+
+    private def Type getActualType(ParameterizedType ptype) {
+        return ptype.getActualTypeArguments.get(0)
+    }
+
     /**
      * Template method which generates setter methods
      *
      * @return string with the setter methods
      */
     def private generateSetters() '''
-        «FOR field : properties SEPARATOR '\n'»
-             «/* FIXME: generate checkers as simple blocks and embed them directly in setters  */»
-             «val restrictions = field.returnType.restrictions»
-             «IF !(field.returnType instanceof GeneratedType) && restrictions !== null»
-                    «IF restrictions.rangeConstraint.present»
-                        «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
-                        «rangeGenerator.generateRangeChecker(field.name.toFirstUpper, restrictions.rangeConstraint.get)»
-
-                    «ENDIF»
-                    «IF restrictions.lengthConstraint.present»
-                    «LengthGenerator.generateLengthChecker(field.fieldName.toString, field.returnType, restrictions.lengthConstraint.get)»
-
-                    «ENDIF»
+        «FOR property : properties»
+            «IF property.returnType instanceof ParameterizedType
+                    && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
+                «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
+            «ELSE»
+                «generateSetter(property, property.returnType)»
             «ENDIF»
-            public «type.name»«BUILDER» set«field.name.toFirstUpper»(final «field.returnType.importedName» value) {
-            «IF !(field.returnType instanceof GeneratedType) && restrictions !== null»
-                «IF restrictions !== null && (restrictions.rangeConstraint.present || restrictions.lengthConstraint.present)»
-                if (value != null) {
-                    «IF restrictions.rangeConstraint.present»
-                        «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
-                        «IF field.returnType instanceof ConcreteType»
-                            «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value")»
-                        «ELSE»
-                            «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value.getValue()")»
-                        «ENDIF»
-                    «ENDIF»
-                    «IF restrictions.lengthConstraint.present»
-                        «IF field.returnType instanceof ConcreteType»
-                            «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value")»
-                         «ELSE»
-                            «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, "value.getValue()")»
-                        «ENDIF»
-                    «ENDIF»
-                }
-                «ENDIF»
-            «ENDIF»
-                this.«field.fieldName» = value;
-                return this;
-            }
         «ENDFOR»
-        «IF augmentField !== null»
 
+        «IF augmentField !== null»
             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
                 if (augmentationValue == null) {
                     return remove«augmentField.name.toFirstUpper»(augmentationType);
@@ -537,17 +596,7 @@ class BuilderTemplate extends BaseTemplate {
             «ENDFOR»
             «IF augmentField !== null»
                 «IF impl»
-                    switch (base.«augmentField.name».size()) {
-                    case 0:
-                        this.«augmentField.name» = «Collections.importedName».emptyMap();
-                        break;
-                    case 1:
-                        final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
-                        this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
-                        break;
-                    default :
-                        this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
-                    }
+                    this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»);
                 «ELSE»
                     if (base instanceof «type.name»«IMPL») {
                         «type.name»«IMPL» impl = («type.name»«IMPL») base;
@@ -613,10 +662,7 @@ class BuilderTemplate extends BaseTemplate {
             @SuppressWarnings("unchecked")
             «IF addOverride»@Override«ENDIF»
             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
-                if (augmentationType == null) {
-                    throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
-                }
-                return (E) «augmentField.name».get(augmentationType);
+                return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
             }
         «ENDIF»
     '''
@@ -713,39 +759,17 @@ class BuilderTemplate extends BaseTemplate {
     '''
 
     def override generateToString(Collection<GeneratedProperty> properties) '''
-        «IF !(properties === null)»
+        «IF properties !== null»
             @Override
             public «String.importedName» toString() {
-                «String.importedName» name = "«type.name» [";
-                «StringBuilder.importedName» builder = new «StringBuilder.importedName» (name);
-                «FOR property : properties SEPARATOR "\n    builder.append(\", \");\n}" AFTER "    }\n"»
-                    if («property.fieldName» != null) {
-                        builder.append("«property.fieldName»=");
-                        «IF property.returnType.name.contains("[")»
-                            builder.append(«Arrays.importedName».toString(«property.fieldName»));
-                        «ELSE»
-                            builder.append(«property.fieldName»);
-                        «ENDIF»
+                final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
+                «FOR property : properties»
+                    «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
                 «ENDFOR»
                 «IF augmentField !== null»
-                    «IF !properties.empty»
-                «««Append comma separator only if it's not there already from previous operation»»»
-final int builderLength = builder.length();
-                    final int builderAdditionalLength = builder.substring(name.length(), builderLength).length();
-                    if (builderAdditionalLength > 2 && !builder.substring(builderLength - 2, builderLength).equals(", ")) {
-                        builder.append(", ");
-                    }
-                    «ENDIF»
-                    builder.append("«augmentField.name»=");
-                    builder.append(«augmentField.name».values());«"\n"»
-                    return builder.append(']').toString();
-                «ELSE»
-                    «IF properties.empty»
-                    return builder.append(']').toString();
-                    «ELSE»
-            return builder.append(']').toString();
-                    «ENDIF»
+                    «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
                 «ENDIF»
+                return helper.toString();
             }
         «ENDIF»
     '''