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 18d6730bd10dd0953ec60d6ddfc3e83ca485ffb1..3d0d19c436b2889f9eb5b15f664c88580d149b49 100644 (file)
-package org.opendaylight.yangtools.sal.java.api.generator\r
-\r
-import java.util.LinkedHashSet\r
-import java.util.List\r
-import java.util.Map\r
-import java.util.Set\r
-import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl\r
-import org.opendaylight.yangtools.binding.generator.util.Types\r
-import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl\r
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty\r
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject\r
-import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType\r
-import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature\r
-import org.opendaylight.yangtools.sal.binding.model.api.Type\r
-import org.opendaylight.yangtools.yang.binding.Augmentable\r
-import static org.opendaylight.yangtools.binding.generator.util.Types.*\r
-import java.util.HashMap\r
-import java.util.Collections\r
-\r
-/**\r
- * Template for generating JAVA builder classes. \r
- */\r
-\r
-class BuilderTemplate extends BaseTemplate {\r
-    \r
-    /**\r
-     * Constant with the name of the concrete method.\r
-     */\r
-    val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"\r
-    \r
-    /**\r
-     * Constant with the suffix for builder classes.\r
-     */\r
-    val static BUILDER = 'Builder'\r
-    \r
-    /**\r
-     * Constant with suffix for the classes which are generated from the builder classes.\r
-     */\r
-    val static IMPL = 'Impl'\r
-    \r
-    /**\r
-     * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME\r
-     */\r
-    var GeneratedProperty augmentField\r
-    \r
-    /**\r
-     * Set of class attributes (fields) which are derived from the getter methods names\r
-     */\r
-    val Set<GeneratedProperty> properties\r
-    \r
-    /**\r
-     * Constructs new instance of this class.\r
-     * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>\r
-     */\r
-    new(GeneratedType genType) {\r
-        super(genType)\r
-        this.properties = propertiesFromMethods(createMethods)\r
-    }\r
-    \r
-    /**\r
-     * Returns set of method signature instances which contains all the methods of the <code>genType</code>\r
-     * and all the methods of the implemented interfaces.\r
-     * \r
-     * @returns set of method signature instances\r
-     */\r
-    def private Set<MethodSignature> createMethods() {\r
-        val Set<MethodSignature> methods = new LinkedHashSet\r
-        methods.addAll(type.methodDefinitions)\r
-        collectImplementedMethods(methods, type.implements)\r
-        return methods\r
-    }\r
-    \r
-\r
-    /**\r
-     * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code> \r
-     * and recursivelly their implemented interfaces.\r
-     * \r
-     * @param methods set of method signatures\r
-     * @param implementedIfcs list of implemented interfaces\r
-     */\r
-    def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {\r
-        if (implementedIfcs == null || implementedIfcs.empty) {\r
-            return\r
-        }\r
-        for (implementedIfc : implementedIfcs) {\r
-            if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {\r
-                val ifc = implementedIfc as GeneratedType\r
-                methods.addAll(ifc.methodDefinitions)\r
-                collectImplementedMethods(methods, ifc.implements)\r
-            } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {\r
-                for (m : Augmentable.methods) {\r
-                    if (m.name == GET_AUGMENTATION_METHOD_NAME) {\r
-                        //addToImports(JAVA_UTIL, HASH_MAP)\r
-                        //addToImports(JAVA_UTIL, MAP)\r
-                        val fullyQualifiedName = m.returnType.name\r
-                        val pkg = fullyQualifiedName.package\r
-                        val name = fullyQualifiedName.name\r
-                        //addToImports(pkg, name)\r
-                        val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)\r
-                        val refType = new ReferencedTypeImpl(pkg, name)\r
-                        val generic = new ReferencedTypeImpl(type.packageName, type.name)\r
-                        val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)\r
-                        tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)\r
-                        augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter\r
-                    }\r
-                }\r
-            }\r
-        }\r
-    }\r
-    \r
-    \r
-    /**\r
-     * Returns the first element of the list <code>elements</code>.\r
-     * \r
-     * @param list of elements\r
-     */\r
-    def private <E> first(List<E> elements) {\r
-        elements.get(0)\r
-    }\r
-    \r
-    /**\r
-     * Returns the name of the package from <code>fullyQualifiedName</code>.\r
-     * \r
-     * @param fullyQualifiedName string with fully qualified type name (package + type)\r
-     * @return string with the package name\r
-     */\r
-    def private String getPackage(String fullyQualifiedName) {\r
-        val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)\r
-        return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)\r
-    }\r
-\r
-       /**\r
-        * Returns the name of tye type from <code>fullyQualifiedName</code>\r
-        * \r
-        * @param fullyQualifiedName string with fully qualified type name (package + type)\r
-        * @return string with the name of the type\r
-        */\r
-    def private String getName(String fullyQualifiedName) {\r
-        val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)\r
-        return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)\r
-    }\r
-    \r
-\r
-    /**\r
-     * Creates set of generated property instances from getter <code>methods</code>.\r
-     * \r
-     * @param set of method signature instances which should be transformed to list of properties \r
-     * @return set of generated property instances which represents the getter <code>methods</code>\r
-     */\r
-    def private propertiesFromMethods(Set<MethodSignature> methods) {\r
-        \r
-\r
-        if (methods == null || methods.isEmpty()) {\r
-            return Collections.emptySet\r
-        }\r
-        val Set<GeneratedProperty> result = new LinkedHashSet\r
-        for (m : methods) {\r
-            val createdField = m.propertyFromGetter\r
-            if (createdField != null) {\r
-                result.add(createdField)\r
-            }\r
-        }\r
-        return result\r
-    }\r
-    \r
-    /**\r
-     * Creates generated property instance from the getter <code>method</code> name and return type.\r
-     * \r
-     * @param method method signature from which is the method name and return type obtained\r
-     * @return generated property instance for the getter <code>method</code>\r
-     * @throws IllegalArgumentException<ul>\r
-     *         <li>if the <code>method</code> equals <code>null</code></li>\r
-     *         <li>if the name of the <code>method</code> equals <code>null</code></li>\r
-     *         <li>if the name of the <code>method</code> is empty</li>\r
-     *         <li>if the return type of the <code>method</code> equals <code>null</code></li>\r
-     * </ul>\r
-     */\r
-    def private GeneratedProperty propertyFromGetter(MethodSignature method) {\r
-\r
-        if (method == null || method.name == null || method.name.empty || method.returnType == null) {\r
-            throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")\r
-        }\r
-        var prefix = "get";\r
-        if(BOOLEAN.equals(method.returnType)) {\r
-            prefix = "is";\r
-        } \r
-        if (method.name.startsWith(prefix)) {\r
-            val fieldName = method.getName().substring(prefix.length()).toFirstLower\r
-            val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")\r
-            tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)\r
-            return tmpGenTO.toInstance.properties.first\r
-        }\r
-    }\r
-\r
-    /**\r
-     * Template method which generates JAVA class body for builder class and for IMPL class. \r
-     * \r
-     * @return string with JAVA source code\r
-     */\r
-    override body() '''\r
-\r
-        public class «type.name»«BUILDER» {\r
-\r
-            «generateFields(false)»\r
-\r
-            «generateGetters(false)»\r
-\r
-            «generateSetters»\r
-\r
-            public «type.name» build() {\r
-                return new «type.name»«IMPL»(this);\r
-            }\r
-\r
-            private static final class «type.name»«IMPL» implements «type.name» {\r
-\r
-                «generateFields(true)»\r
-\r
-                «generateConstructor»\r
-\r
-                «generateGetters(true)»\r
-\r
-                «generateHashCode()»\r
-\r
-                «generateEquals()»\r
-            }\r
-\r
-        }\r
-    '''\r
-\r
-       /**\r
-        * Template method which generates class attributes.\r
-        * \r
-        * @param boolean value which specify whether field is|isn't final\r
-        * @return string with class attributes and their types\r
-        */\r
-    def private generateFields(boolean _final) '''\r
-        «IF !properties.empty»\r
-            «FOR f : properties»\r
-                private  «IF _final»final«ENDIF»  «f.returnType.importedName» «f.fieldName»;\r
-            «ENDFOR»\r
-        «ENDIF»\r
-        «IF augmentField != null»\r
-            private «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();\r
-        «ENDIF»\r
-    '''\r
-\r
-       /**\r
-        * Template method which generates setter methods\r
-        * \r
-        * @return string with the setter methods \r
-        */\r
-    def private generateSetters() '''\r
-        «FOR field : properties SEPARATOR '\n'»\r
-            public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {\r
-                this.«field.fieldName» = value;\r
-                return this;\r
-            }\r
-        «ENDFOR»\r
-        «IF augmentField != null»\r
-            \r
-            public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {\r
-                this.«augmentField.name».put(augmentationType, augmentation);\r
-                return this;\r
-            }\r
-        «ENDIF»\r
-    '''\r
-    \r
-    /**\r
-     * Template method which generate constructor for IMPL class.\r
-     * \r
-     * @return string with IMPL class constructor\r
-     */\r
-    def private generateConstructor() '''\r
-        private «type.name»«IMPL»(«type.name»«BUILDER» builder) {\r
-            «IF !properties.empty»\r
-                «FOR field : properties»\r
-                    this.«field.fieldName» = builder.«field.getterMethodName»();\r
-                «ENDFOR»\r
-            «ENDIF»\r
-            «IF augmentField != null»\r
-                this.«augmentField.name».putAll(builder.«augmentField.name»);\r
-            «ENDIF»\r
-        }\r
-    '''\r
-    \r
-\r
-    /**\r
-     * Template method which generate getter methods for IMPL class.\r
-     * \r
-     * @return string with getter methods\r
-     */\r
-    def private generateGetters(boolean addOverride) '''\r
-        «IF !properties.empty»\r
-            «FOR field : properties SEPARATOR '\n'»\r
-                «IF addOverride»@Override«ENDIF»\r
-                «field.getterMethod»\r
-            «ENDFOR»\r
-        «ENDIF»\r
-        «IF augmentField != null»\r
-\r
-            @SuppressWarnings("unchecked")\r
-            «IF addOverride»@Override«ENDIF»\r
-            public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {\r
-                if (augmentationType == null) {\r
-                    throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");\r
-                }\r
-                return (E) «augmentField.name».get(augmentationType);\r
-            }\r
-        «ENDIF»\r
-    '''\r
-\r
-    /**\r
-     * Template method which generates the method <code>hashCode()</code>.\r
-     * \r
-     * @return string with the <code>hashCode()</code> method definition in JAVA format\r
-     */\r
-    def protected generateHashCode() '''\r
-        «IF !properties.empty || augmentField != null»\r
-            @Override\r
-            public int hashCode() {\r
-                final int prime = 31;\r
-                int result = 1;\r
-                «FOR property : properties»\r
-                    result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());\r
-                «ENDFOR»\r
-                «IF augmentField != null»\r
-                    result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());\r
-                «ENDIF»\r
-                return result;\r
-            }\r
-        «ENDIF»\r
-    '''\r
-\r
-    /**\r
-     * Template method which generates the method <code>equals()</code>.\r
-     * \r
-     * @return string with the <code>equals()</code> method definition in JAVA format     \r
-     */\r
-    def protected generateEquals() '''\r
-        «IF !properties.empty || augmentField != null»\r
-            @Override\r
-            public boolean equals(java.lang.Object obj) {\r
-                if (this == obj) {\r
-                    return true;\r
-                }\r
-                if (obj == null) {\r
-                    return false;\r
-                }\r
-                if (getClass() != obj.getClass()) {\r
-                    return false;\r
-                }\r
-                «type.name»«IMPL» other = («type.name»«IMPL») obj;\r
-                «FOR property : properties»\r
-                    «val fieldName = property.fieldName»\r
-                    if («fieldName» == null) {\r
-                        if (other.«fieldName» != null) {\r
-                            return false;\r
-                        }\r
-                    } else if(!«fieldName».equals(other.«fieldName»)) {\r
-                        return false;\r
-                    }\r
-                «ENDFOR»\r
-                «IF augmentField != null»\r
-                    «val fieldName = augmentField.name»\r
-                    if («fieldName» == null) {\r
-                        if (other.«fieldName» != null) {\r
-                            return false;\r
-                        }\r
-                    } else if(!«fieldName».equals(other.«fieldName»)) {\r
-                        return false;\r
-                    }\r
-                «ENDIF»\r
-                return true;\r
-            }\r
-        «ENDIF»\r
-    '''\r
-\r
-    override protected getFullyQualifiedName() {\r
-        '''«type.fullyQualifiedName»Builder'''.toString\r
-    }\r
-    \r
-}\r
-\r
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.sal.java.api.generator
+
+import java.util.Arrays;
+import java.util.LinkedHashSet
+import java.util.List
+import java.util.Map
+import java.util.Set
+import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
+import org.opendaylight.yangtools.binding.generator.util.Types
+import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
+import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
+import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
+import org.opendaylight.yangtools.sal.binding.model.api.Type
+import org.opendaylight.yangtools.yang.binding.Augmentable
+import static org.opendaylight.yangtools.binding.generator.util.Types.*
+import java.util.HashMap
+import java.util.Collections
+import org.opendaylight.yangtools.yang.binding.DataObject
+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. 
+ */
+
+class BuilderTemplate extends BaseTemplate {
+
+    /**
+     * Constant with the name of the concrete method.
+     */
+    val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
+
+    /**
+     * Constant with the suffix for builder classes.
+     */
+    val static BUILDER = 'Builder'
+
+    /**
+     * Constant with suffix for the classes which are generated from the builder classes.
+     */
+    val static IMPL = 'Impl'
+
+    /**
+     * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
+     */
+    var GeneratedProperty augmentField
+
+    /**
+     * Set of class attributes (fields) which are derived from the getter methods names
+     */
+    val Set<GeneratedProperty> properties
+
+    /**
+     * Constructs new instance of this class.
+     * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
+     */
+    new(GeneratedType genType) {
+        super(genType)
+        this.properties = propertiesFromMethods(createMethods)
+    }
+
+    /**
+     * Returns set of method signature instances which contains all the methods of the <code>genType</code>
+     * and all the methods of the implemented interfaces.
+     * 
+     * @returns set of method signature instances
+     */
+    def private Set<MethodSignature> createMethods() {
+        val Set<MethodSignature> methods = new LinkedHashSet
+        methods.addAll(type.methodDefinitions)
+        collectImplementedMethods(methods, type.implements)
+        return methods
+    }
+
+    /**
+     * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code> 
+     * and recursivelly their implemented interfaces.
+     * 
+     * @param methods set of method signatures
+     * @param implementedIfcs list of implemented interfaces
+     */
+    def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
+        if (implementedIfcs == null || implementedIfcs.empty) {
+            return
+        }
+        for (implementedIfc : implementedIfcs) {
+            if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
+                val ifc = implementedIfc as GeneratedType
+                methods.addAll(ifc.methodDefinitions)
+                collectImplementedMethods(methods, ifc.implements)
+            } 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 parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
+                        tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
+                        augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the first element of the list <code>elements</code>.
+     * 
+     * @param list of elements
+     */
+    def private <E> first(List<E> elements) {
+        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>.
+     * 
+     * @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(Collection<MethodSignature> methods) {
+        if (methods == null || methods.isEmpty()) {
+            return Collections.emptySet
+        }
+        val Set<GeneratedProperty> result = new LinkedHashSet
+        for (m : methods) {
+            val createdField = m.propertyFromGetter
+            if (createdField != null) {
+                result.add(createdField)
+            }
+        }
+        return result
+    }
+
+    /**
+     * Creates generated property instance from the getter <code>method</code> name and return type.
+     * 
+     * @param method method signature from which is the method name and return type obtained
+     * @return generated property instance for the getter <code>method</code>
+     * @throws IllegalArgumentException<ul>
+     *         <li>if the <code>method</code> equals <code>null</code></li>
+     *         <li>if the name of the <code>method</code> equals <code>null</code></li>
+     *         <li>if the name of the <code>method</code> is empty</li>
+     *         <li>if the return type of the <code>method</code> equals <code>null</code></li>
+     * </ul>
+     */
+    def private GeneratedProperty propertyFromGetter(MethodSignature method) {
+        if (method == null || method.name == null || method.name.empty || method.returnType == null) {
+            throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
+        }
+        var prefix = "get";
+        if(BOOLEAN.equals(method.returnType)) {
+            prefix = "is";
+        } 
+        if (method.name.startsWith(prefix)) {
+            val fieldName = method.getName().substring(prefix.length()).toFirstLower
+            val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
+            tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
+            return tmpGenTO.toInstance.properties.first
+        }
+    }
+
+    /**
+     * Template method which generates JAVA class body for builder class and for IMPL class. 
+     * 
+     * @return string with JAVA source code
+     */
+    override body() '''
+
+        public class «type.name»«BUILDER» {
+
+            «generateFields(false)»
+
+            «generateAugmentField(true)»
+
+            «generateConstructorsFromIfcs(type)»
+
+            «generateCopyConstructor(false)»
+
+            «generateMethodFieldsFrom(type)»
+
+            «generateGetters(false)»
+
+            «generateSetters»
+
+            public «type.name» build() {
+                return new «type.name»«IMPL»(this);
+            }
+
+            private static final class «type.name»«IMPL» implements «type.name» {
+
+                «implementedInterfaceGetter»
+
+                «generateFields(true)»
+
+                «generateAugmentField(false)»
+
+                «generateCopyConstructor(true)»
+
+                «generateGetters(true)»
+
+                «generateHashCode()»
+
+                «generateEquals()»
+                
+                «generateToString(properties)»
+            }
+
+        }
+    '''
+
+    /**
+     * Generate default constructor and constructor for every implemented interface from uses statements.
+     */
+    def private generateConstructorsFromIfcs(Type type) '''
+        public «type.name»«BUILDER»() {
+        } 
+        «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
+            «val ifc = type as GeneratedType»
+            «FOR impl : ifc.implements»
+                «generateConstructorFromIfc(impl)»
+            «ENDFOR»
+        «ENDIF»
+    '''
+
+    /**
+     * Generate constructor with argument of given type.
+     */
+    def private Object generateConstructorFromIfc(Type impl) '''
+        «IF (impl instanceof GeneratedType)»
+            «val implType = impl as GeneratedType»
+
+            «IF !(implType.methodDefinitions.empty)»
+                public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {
+                    «printConstructorPropertySetter(implType)»
+                }
+            «ENDIF»
+            «FOR implTypeImplement : implType.implements»
+                «generateConstructorFromIfc(implTypeImplement)»
+            «ENDFOR»
+        «ENDIF»
+    '''
+
+    def private Object printConstructorPropertySetter(Type implementedIfc) '''
+        «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
+            «val ifc = implementedIfc as GeneratedType»
+            «FOR getter : ifc.methodDefinitions»
+                this._«getter.propertyNameFromGetter» = arg.«getter.name»();
+            «ENDFOR»
+            «FOR impl : ifc.implements»
+                «printConstructorPropertySetter(impl)»
+            «ENDFOR»
+        «ENDIF»
+    '''
+
+    /**
+     * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
+     */
+    def private generateMethodFieldsFrom(Type type) '''
+        «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
+            «val ifc = type as GeneratedType»
+            «IF ifc.hasImplementsFromUses»
+                «val List<Type> done = ifc.getBaseIfcs»
+                «generateMethodFieldsFromComment(ifc)»
+                public void fieldsFrom(«DataObject.importedName» arg) {
+                    boolean isValidArg = false;
+                    «FOR impl : ifc.getAllIfcs»
+                        «generateIfCheck(impl, done)»
+                    «ENDFOR»
+                    if (!isValidArg) {
+                        throw new IllegalArgumentException(
+                          "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
+                          "but was: " + arg
+                        );
+                    }
+                }
+            «ENDIF»
+        «ENDIF»
+    '''
+
+    def private generateMethodFieldsFromComment(GeneratedType type) '''
+        /**
+         Set fields from given grouping argument. Valid argument is instance of one of following types:
+         * <ul>
+         «FOR impl : type.getAllIfcs»
+         * <li>«impl.fullyQualifiedName»</li>
+         «ENDFOR»
+         * </ul>
+         *
+         * @param arg grouping object
+         * @throws IllegalArgumentException if given argument is none of valid types
+        */
+    '''
+
+    /**
+     * Method is used to find out if given type implements any interface from uses.
+     */
+    def boolean hasImplementsFromUses(GeneratedType type) {
+        var i = 0
+        for (impl : type.getAllIfcs) {
+            if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
+                i = i + 1
+            }
+        }
+        return i > 0
+    }
+
+    def private generateIfCheck(Type impl, List<Type> done) '''
+        «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
+            «val implType = impl as GeneratedType»
+            if (arg instanceof «implType.fullyQualifiedName») {
+                «printPropertySetter(implType)»
+                isValidArg = true;
+            }
+        «ENDIF»
+    '''
+
+    def private printPropertySetter(Type implementedIfc) '''
+        «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
+        «val ifc = implementedIfc as GeneratedType»
+        «FOR getter : ifc.methodDefinitions»
+            this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
+        «ENDFOR»
+        «ENDIF»
+    '''
+
+    private def List<Type> getBaseIfcs(GeneratedType type) {
+        val List<Type> baseIfcs = new ArrayList();
+        for (ifc : type.implements) {
+            if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
+                baseIfcs.add(ifc)
+            }
+        }
+        return baseIfcs 
+    }
+
+    private def Set<Type> getAllIfcs(Type type) {
+        val Set<Type> baseIfcs = new HashSet()
+        if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
+            val ifc = type as GeneratedType
+            for (impl : ifc.implements) {
+                if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
+                    baseIfcs.add(impl)
+                }
+                baseIfcs.addAll(impl.getAllIfcs)
+            }
+        }
+        return baseIfcs 
+    }
+
+    private def List<String> toListOfNames(Collection<Type> types) {
+        val List<String> names = new ArrayList
+        for (type : types) {
+            names.add(type.fullyQualifiedName)
+        }
+        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
+     */
+    def private generateFields(boolean _final) '''
+        «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.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
+     */
+    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", 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.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
+                this.«augmentField.name».put(augmentationType, augmentation);
+                return this;
+            }
+        «ENDIF»
+    '''
+
+    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»
+            «IF isList && keyType != null»
+                «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
+                «Collections.sort(keyProps,
+                    [ p1, p2 |
+                        return p1.name.compareTo(p2.name)
+                    ])
+                »
+                «FOR field : keyProps»
+                    «removeProperty(allProps, field.name)»
+                «ENDFOR»
+                «removeProperty(allProps, "key")»
+                if (base.getKey() == null) {
+                    this._key = new «keyType.importedName»(
+                        «FOR keyProp : keyProps SEPARATOR ", "»
+                            base.«keyProp.getterMethodName»()
+                        «ENDFOR»
+                    );
+                    «FOR field : keyProps»
+                        this.«field.fieldName» = base.«field.getterMethodName»();
+                    «ENDFOR»
+                } else {
+                    this._key = base.getKey();
+                    «FOR field : keyProps»
+                           this.«field.fieldName» = _key.«field.getterMethodName»();
+                    «ENDFOR»
+                }
+            «ENDIF»
+            «FOR field : allProps»
+                this.«field.fieldName» = base.«field.getterMethodName»();
+            «ENDFOR»
+            «IF augmentField != null»
+                «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»
+        }
+    '''
+
+    private def boolean implementsIfc(GeneratedType type, Type impl) {
+        for (Type ifc : type.implements) {
+            if (ifc.equals(impl)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private def Type getKey(GeneratedType type) {
+        for (m : type.methodDefinitions) {
+            if ("getKey".equals(m.name)) {
+                return m.returnType;
+            }
+        }
+        return null;
+    }
+
+    private def void removeProperty(Collection<GeneratedProperty> props, String name) {
+        var GeneratedProperty toRemove = null
+        for (p : props) {
+            if (p.name.equals(name)) {
+                toRemove = p;
+            }
+        }
+        if (toRemove != null) {
+            props.remove(toRemove);
+        }
+    }
+
+    /**
+     * Template method which generate getter methods for IMPL class.
+     * 
+     * @return string with getter methods
+     */
+    def private generateGetters(boolean addOverride) '''
+        «IF !properties.empty»
+            «FOR field : properties SEPARATOR '\n'»
+                «IF addOverride»@Override«ENDIF»
+                «field.getterMethod»
+            «ENDFOR»
+        «ENDIF»
+        «IF augmentField != null»
+
+            @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);
+            }
+        «ENDIF»
+    '''
+
+    /**
+     * Template method which generates the method <code>hashCode()</code>.
+     * 
+     * @return string with the <code>hashCode()</code> method definition in JAVA format
+     */
+    def protected generateHashCode() '''
+        «IF !properties.empty || augmentField != null»
+            @Override
+            public int hashCode() {
+                final int prime = 31;
+                int result = 1;
+                «FOR property : properties»
+                    «IF property.returnType.name.contains("[")»
+                    result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
+                    «ELSE»
+                    result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
+                    «ENDIF»
+                «ENDFOR»
+                «IF augmentField != null»
+                    result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
+                «ENDIF»
+                return result;
+            }
+        «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() '''
+        «IF !properties.empty || augmentField != null»
+            @Override
+            public boolean equals(«Object.importedName» obj) {
+                if (this == obj) {
+                    return true;
+                }
+                if (obj == null) {
+                    return false;
+                }
+                if (getClass() != obj.getClass()) {
+                    return false;
+                }
+                «type.name»«IMPL» other = («type.name»«IMPL») obj;
+                «FOR property : properties»
+                    «val fieldName = property.fieldName»
+                    if («fieldName» == null) {
+                        if (other.«fieldName» != null) {
+                            return false;
+                        }
+                    «IF property.returnType.name.contains("[")»
+                    } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
+                    «ELSE»
+                    } else if(!«fieldName».equals(other.«fieldName»)) {
+                    «ENDIF»
+                        return false;
+                    }
+                «ENDFOR»
+                «IF augmentField != null»
+                    «val fieldName = augmentField.name»
+                    if («fieldName» == null) {
+                        if (other.«fieldName» != null) {
+                            return false;
+                        }
+                    } else if(!«fieldName».equals(other.«fieldName»)) {
+                        return false;
+                    }
+                «ENDIF»
+                return true;
+            }
+        «ENDIF»
+    '''
+
+    def override generateToString(Collection<GeneratedProperty> properties) '''
+        «IF !(properties === null)»
+            @Override
+            public «String.importedName» toString() {
+                «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
+                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»
+                «IF augmentField != null»
+                    if (first) {
+                        first = false;
+                    } else {
+                        builder.append(", ");
+                    }
+                    builder.append("«augmentField.name»=");
+                    builder.append(«augmentField.name».values());
+                «ENDIF»
+                return builder.append(']').toString();
+            }
+        «ENDIF»
+    '''
+
+    override protected getFullyQualifiedName() {
+        '''«type.fullyQualifiedName»Builder'''.toString
+    }
+
+    def implementedInterfaceGetter() '''
+    public «Class.importedName»<«type.importedName»> getImplementedInterface() {
+        return «type.importedName».class;
+    }
+    '''
+
+}
+