BUG-532: implemented copy constructor for generated builder classes.
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BuilderTemplate.xtend
index b4940a97c385207bbaf515c408a6cc619b86996f..3d0d19c436b2889f9eb5b15f664c88580d149b49 100644 (file)
@@ -29,6 +29,8 @@ import java.util.ArrayList
 import java.util.HashSet
 import java.util.Collection
 import org.opendaylight.yangtools.yang.binding.Identifiable
+import com.google.common.collect.Range
+import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
 
 /**
  * Template for generating JAVA builder classes. 
@@ -154,7 +156,7 @@ class BuilderTemplate extends BaseTemplate {
      * @param set of method signature instances which should be transformed to list of properties 
      * @return set of generated property instances which represents the getter <code>methods</code>
      */
-    def private propertiesFromMethods(Set<MethodSignature> methods) {
+    def private propertiesFromMethods(Collection<MethodSignature> methods) {
         if (methods == null || methods.isEmpty()) {
             return Collections.emptySet
         }
@@ -207,8 +209,12 @@ class BuilderTemplate extends BaseTemplate {
 
             «generateFields(false)»
 
+            «generateAugmentField(true)»
+
             «generateConstructorsFromIfcs(type)»
 
+            «generateCopyConstructor(false)»
+
             «generateMethodFieldsFrom(type)»
 
             «generateGetters(false)»
@@ -225,7 +231,9 @@ class BuilderTemplate extends BaseTemplate {
 
                 «generateFields(true)»
 
-                «generateConstructor»
+                «generateAugmentField(false)»
+
+                «generateCopyConstructor(true)»
 
                 «generateGetters(true)»
 
@@ -256,7 +264,7 @@ class BuilderTemplate extends BaseTemplate {
     /**
      * Generate constructor with argument of given type.
      */
-    def private generateConstructorFromIfc(Type impl) '''
+    def private Object generateConstructorFromIfc(Type impl) '''
         «IF (impl instanceof GeneratedType)»
             «val implType = impl as GeneratedType»
 
@@ -271,7 +279,7 @@ class BuilderTemplate extends BaseTemplate {
         «ENDIF»
     '''
 
-    def private printConstructorPropertySetter(Type implementedIfc) '''
+    def private Object printConstructorPropertySetter(Type implementedIfc) '''
         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
             «val ifc = implementedIfc as GeneratedType»
             «FOR getter : ifc.methodDefinitions»
@@ -386,53 +394,117 @@ class BuilderTemplate extends BaseTemplate {
         return names
     }
 
-       /**
-        * Template method which generates class attributes.
-        * 
-        * @param boolean value which specify whether field is|isn't final
-        * @return string with class attributes and their types
-        */
+    /**
+     * Template method which generates class attributes.
+     *
+     * @param boolean value which specify whether field is|isn't final
+     * @return string with class attributes and their types
+     */
     def private generateFields(boolean _final) '''
-        «IF !properties.empty»
+        «IF properties !== null»
             «FOR f : properties»
                 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
+                «val restrictions = f.returnType.restrictions»
+                «IF !_final && restrictions != null»
+                    «IF !(restrictions.lengthConstraints.empty)»
+                        private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_length;
+                    «ENDIF»
+                    «IF !(restrictions.rangeConstraints.empty)»
+                        private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_range;
+                    «ENDIF»
+                «ENDIF»
             «ENDFOR»
         «ENDIF»
+    '''
+
+    def private generateAugmentField(boolean init) '''
         «IF augmentField != null»
-            private «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
+            private «Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
         «ENDIF»
     '''
 
-       /**
-        * Template method which generates setter methods
-        * 
-        * @return string with the setter methods 
-        */
+    /**
+     * Template method which generates setter methods
+     *
+     * @return string with the setter methods
+     */
     def private generateSetters() '''
         «FOR field : properties SEPARATOR '\n'»
+            «val length = field.fieldName + "_length"»
+            «val range = field.fieldName + "_range"»
             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
-                «generateRestrictions(field, "value")»
-
+                «generateRestrictions(field, "value", length, range)»
                 this.«field.fieldName» = value;
                 return this;
             }
+            «generateLengthMethod(length, field.returnType, type.name+BUILDER, length)»
+            «generateRangeMethod(range, field.returnType.restrictions, field.returnType, type.name+BUILDER, range)»
         «ENDFOR»
         «IF augmentField != null»
 
-            public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
+            public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
                 this.«augmentField.name».put(augmentationType, augmentation);
                 return this;
             }
         «ENDIF»
     '''
 
-    /**
-     * Template method which generate constructor for IMPL class.
-     * 
-     * @return string with IMPL class constructor
-     */
-    def private generateConstructor() '''
-        private «type.name»«IMPL»(«type.name»«BUILDER» builder) {
+    def generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter, String rangeGetter) '''
+        «val Type type = field.returnType»
+        «IF type instanceof ConcreteType»
+            «createRestrictions(type, paramName, type.name.contains("["), lengthGetter, rangeGetter)»
+        «ELSEIF type instanceof GeneratedTransferObject»
+            «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter, rangeGetter)»
+        «ENDIF»
+    '''
+
+    def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter, String rangeGetter) '''
+        «val restrictions = type.getRestrictions»
+        «IF restrictions !== null»
+            «val boolean isNestedType = !(type instanceof ConcreteType)»
+            «IF !restrictions.lengthConstraints.empty»
+                «generateLengthRestriction(type, paramName, lengthGetter, isNestedType, isArray)»
+            «ENDIF»
+            «IF !restrictions.rangeConstraints.empty»
+                «generateRangeRestriction(type, paramName, rangeGetter, isNestedType)»
+            «ENDIF»
+        «ENDIF»
+    '''
+
+    def private generateLengthRestriction(Type type, String paramName, String getterName, boolean isNestedType, boolean isArray) '''
+        «val restrictions = type.getRestrictions»
+        if («paramName» != null) {
+            «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
+            «printLengthConstraint(type, clazz, paramName, isNestedType, isArray)»
+            boolean isValidLength = false;
+            for («Range.importedName»<«clazz.importedNumber»> r : «getterName»()) {
+                if (r.contains(_constraint)) {
+                    isValidLength = true;
+                }
+            }
+            if (!isValidLength) {
+                throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «getterName»));
+            }
+        }
+    '''
+
+    def private generateRangeRestriction(Type type, String paramName, String getterName, boolean isNestedType) '''
+        if («paramName» != null) {
+            «printRangeConstraint(type, paramName, isNestedType)»
+            boolean isValidRange = false;
+            for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
+                if (r.contains(_constraint)) {
+                    isValidRange = true;
+                }
+            }
+            if (!isValidRange) {
+                throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
+            }
+        }
+    '''
+
+    def private CharSequence generateCopyConstructor(boolean impl) '''
+        «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
             «val allProps = new ArrayList(properties)»
             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
             «val keyType = type.getKey»
@@ -447,27 +519,41 @@ class BuilderTemplate extends BaseTemplate {
                     «removeProperty(allProps, field.name)»
                 «ENDFOR»
                 «removeProperty(allProps, "key")»
-                if (builder.getKey() == null) {
+                if (base.getKey() == null) {
                     this._key = new «keyType.importedName»(
                         «FOR keyProp : keyProps SEPARATOR ", "»
-                            builder.«keyProp.getterMethodName»()
+                            base.«keyProp.getterMethodName»()
                         «ENDFOR»
                     );
                     «FOR field : keyProps»
-                        this.«field.fieldName» = builder.«field.getterMethodName»();
+                        this.«field.fieldName» = base.«field.getterMethodName»();
                     «ENDFOR»
                 } else {
-                    this._key = builder.getKey();
+                    this._key = base.getKey();
                     «FOR field : keyProps»
                            this.«field.fieldName» = _key.«field.getterMethodName»();
                     «ENDFOR»
                 }
             «ENDIF»
             «FOR field : allProps»
-                this.«field.fieldName» = builder.«field.getterMethodName»();
+                this.«field.fieldName» = base.«field.getterMethodName»();
             «ENDFOR»
             «IF augmentField != null»
-                this.«augmentField.name».putAll(builder.«augmentField.name»);
+                «IF !impl»if (base instanceof «type.name»«IMPL») {«ENDIF»
+                    «IF !impl»«type.name»«IMPL» _impl = («type.name»«IMPL») base;«ENDIF»
+                    «val prop = if (impl) "base" else "_impl"»
+                    switch («prop».«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 = «prop».«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»<>(«prop».«augmentField.name»);
+                    }
+                «IF !impl»}«ENDIF»
             «ENDIF»
         }
     '''
@@ -518,7 +604,7 @@ class BuilderTemplate extends BaseTemplate {
 
             @SuppressWarnings("unchecked")
             «IF addOverride»@Override«ENDIF»
-            public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {
+            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!");
                 }
@@ -561,7 +647,7 @@ class BuilderTemplate extends BaseTemplate {
     def protected generateEquals() '''
         «IF !properties.empty || augmentField != null»
             @Override
-            public boolean equals(java.lang.Object obj) {
+            public boolean equals(«Object.importedName» obj) {
                 if (this == obj) {
                     return true;
                 }
@@ -602,10 +688,10 @@ class BuilderTemplate extends BaseTemplate {
     '''
 
     def override generateToString(Collection<GeneratedProperty> properties) '''
-        «IF !properties.empty»
+        «IF !(properties === null)»
             @Override
-            public String toString() {
-                StringBuilder builder = new StringBuilder("«type.name» [");
+            public «String.importedName» toString() {
+                «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
                 boolean first = true;
 
                 «FOR property : properties»