Merge "Added tests for yang.model.util"
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BuilderTemplate.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.sal.java.api.generator
9
10 import com.google.common.collect.ImmutableSortedSet
11 import com.google.common.collect.Range
12 import java.util.ArrayList
13 import java.util.Arrays
14 import java.util.Collection
15 import java.util.Collections
16 import java.util.HashMap
17 import java.util.HashSet
18 import java.util.LinkedHashSet
19 import java.util.List
20 import java.util.Map
21 import java.util.Set
22 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
23 import org.opendaylight.yangtools.binding.generator.util.Types
24 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
25 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
26 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
28 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
29 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
30 import org.opendaylight.yangtools.sal.binding.model.api.Type
31 import org.opendaylight.yangtools.yang.binding.Augmentable
32 import org.opendaylight.yangtools.yang.binding.ChildOf
33 import org.opendaylight.yangtools.yang.binding.DataObject
34 import org.opendaylight.yangtools.yang.binding.Identifiable
35
36 /**
37  * Template for generating JAVA builder classes. 
38  */
39
40 class BuilderTemplate extends BaseTemplate {
41
42     /**
43      * Constant with the name of the concrete method.
44      */
45     val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
46
47     /**
48      * Constant with the suffix for builder classes.
49      */
50     val static BUILDER = 'Builder'
51
52     /**
53      * Constant with suffix for the classes which are generated from the builder classes.
54      */
55     val static IMPL = 'Impl'
56
57     /**
58      * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
59      */
60     var GeneratedProperty augmentField
61
62     /**
63      * Set of class attributes (fields) which are derived from the getter methods names
64      */
65     val Set<GeneratedProperty> properties
66
67     private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
68
69     /**
70      * Constructs new instance of this class.
71      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
72      */
73     new(GeneratedType genType) {
74         super(genType)
75         this.properties = propertiesFromMethods(createMethods)
76     }
77
78     /**
79      * Returns set of method signature instances which contains all the methods of the <code>genType</code>
80      * and all the methods of the implemented interfaces.
81      * 
82      * @returns set of method signature instances
83      */
84     def private Set<MethodSignature> createMethods() {
85         val Set<MethodSignature> methods = new LinkedHashSet();
86         methods.addAll(type.methodDefinitions)
87         collectImplementedMethods(methods, type.implements)
88         val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
89
90         return sortedMethods
91     }
92
93     /**
94      * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code> 
95      * and recursively their implemented interfaces.
96      * 
97      * @param methods set of method signatures
98      * @param implementedIfcs list of implemented interfaces
99      */
100     def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
101         if (implementedIfcs == null || implementedIfcs.empty) {
102             return
103         }
104         for (implementedIfc : implementedIfcs) {
105             if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
106                 val ifc = implementedIfc as GeneratedType
107                 methods.addAll(ifc.methodDefinitions)
108                 collectImplementedMethods(methods, ifc.implements)
109             } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
110                 for (m : Augmentable.methods) {
111                     if (m.name == GET_AUGMENTATION_METHOD_NAME) {
112                         val fullyQualifiedName = m.returnType.name
113                         val pkg = fullyQualifiedName.package
114                         val name = fullyQualifiedName.name
115                         val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
116                         val refType = new ReferencedTypeImpl(pkg, name)
117                         val generic = new ReferencedTypeImpl(type.packageName, type.name)
118                         val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
119                         tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
120                         augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
121                     }
122                 }
123             }
124         }
125     }
126
127     /**
128      * Returns the first element of the list <code>elements</code>.
129      * 
130      * @param list of elements
131      */
132     def private <E> first(List<E> elements) {
133         elements.get(0)
134     }
135
136     /**
137      * Returns the name of the package from <code>fullyQualifiedName</code>.
138      * 
139      * @param fullyQualifiedName string with fully qualified type name (package + type)
140      * @return string with the package name
141      */
142     def private String getPackage(String fullyQualifiedName) {
143         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
144         return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
145     }
146
147     /**
148      * Returns the name of tye type from <code>fullyQualifiedName</code>
149      *
150      * @param fullyQualifiedName string with fully qualified type name (package + type)
151      * @return string with the name of the type
152      */
153     def private String getName(String fullyQualifiedName) {
154         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
155         return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
156     }
157
158     /**
159      * Creates set of generated property instances from getter <code>methods</code>.
160      * 
161      * @param set of method signature instances which should be transformed to list of properties 
162      * @return set of generated property instances which represents the getter <code>methods</code>
163      */
164     def private propertiesFromMethods(Collection<MethodSignature> methods) {
165         if (methods == null || methods.isEmpty()) {
166             return Collections.emptySet
167         }
168         val Set<GeneratedProperty> result = new LinkedHashSet
169         for (m : methods) {
170             val createdField = m.propertyFromGetter
171             if (createdField != null) {
172                 result.add(createdField)
173             }
174         }
175         return result
176     }
177
178     /**
179      * Creates generated property instance from the getter <code>method</code> name and return type.
180      * 
181      * @param method method signature from which is the method name and return type obtained
182      * @return generated property instance for the getter <code>method</code>
183      * @throws IllegalArgumentException<ul>
184      *  <li>if the <code>method</code> equals <code>null</code></li>
185      *  <li>if the name of the <code>method</code> equals <code>null</code></li>
186      *  <li>if the name of the <code>method</code> is empty</li>
187      *  <li>if the return type of the <code>method</code> equals <code>null</code></li>
188      * </ul>
189      */
190     def private GeneratedProperty propertyFromGetter(MethodSignature method) {
191         if (method == null || method.name == null || method.name.empty || method.returnType == null) {
192             throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
193         }
194         var prefix = "get";
195         if(Types.BOOLEAN.equals(method.returnType)) {
196             prefix = "is";
197         } 
198         if (method.name.startsWith(prefix)) {
199             val fieldName = method.getName().substring(prefix.length()).toFirstLower
200             val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
201             tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
202             return tmpGenTO.toInstance.properties.first
203         }
204     }
205
206     /**
207      * Template method which generates JAVA class body for builder class and for IMPL class. 
208      * 
209      * @return string with JAVA source code
210      */
211     override body() '''
212         «wrapToDocumentation(formatDataForJavaDoc(type))»
213         public class «type.name»«BUILDER» {
214
215             «generateFields(false)»
216
217             «generateAugmentField(false)»
218
219             «generateConstructorsFromIfcs(type)»
220
221             «generateCopyConstructor(false)»
222
223             «generateMethodFieldsFrom(type)»
224
225             «generateGetters(false)»
226
227             «generateSetters»
228
229             «generateBuildMethod»
230
231             «generateBuildBoxedMethod»
232
233             private static final class «type.name»«IMPL» implements «type.name» {
234
235                 «implementedInterfaceGetter»
236
237                 «generateFields(true)»
238
239                 «generateAugmentField(true)»
240
241                 «generateCopyConstructor(true)»
242
243                 «generateGetters(true)»
244
245                 «generateHashCode()»
246
247                 «generateEquals()»
248                 
249                 «generateToString(properties)»
250             }
251
252         }
253     '''
254
255     def private generateBuildMethod() '''
256         public «type.name» build() {
257             return new «type.name»«IMPL»(this);
258         }
259     '''
260
261     def private generateBuildBoxedMethod() {
262         if(type.suitableForBoxing && type.parentType != null && isContainerAndIsNotList(type)) {
263             val parentTypeBuilder = createParentTypeBuilder()
264             if (countMatches(parentTypeBuilder, "org") < 2) {
265                 return '''
266                     public «type.parentType.importedName» buildBoxed() {
267                         return new «parentTypeBuilder»().set«type.name»(build()).build();
268                     }
269                 '''
270             }
271         }
272         return ''
273     }
274
275     def private int countMatches(String string, String subString) {
276         if (string.nullOrEmpty || subString.nullOrEmpty) {
277             return 0
278         }
279         var int count = 0;
280         var int idx = 0;
281         while ((idx = string.indexOf(subString, idx)) != -1) {
282             count = count + 1;
283             idx = idx + subString.length();
284         }
285         return count;
286     }
287
288     def private createParentTypeBuilder() {
289         return type.parentType.packageName + "." + type.parentType.importedName + "Builder"
290     }
291
292     def private boolean isContainerAndIsNotList(GeneratedType type) {
293         val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))
294         val implementsChildOf = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(ChildOf), type))
295
296         if (implementsChildOf && !isList) {
297             return true
298         }
299         return false;
300     }
301
302     /**
303      * Generate default constructor and constructor for every implemented interface from uses statements.
304      */
305     def private generateConstructorsFromIfcs(Type type) '''
306         public «type.name»«BUILDER»() {
307         } 
308         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
309             «val ifc = type as GeneratedType»
310             «FOR impl : ifc.implements»
311                 «generateConstructorFromIfc(impl)»
312             «ENDFOR»
313         «ENDIF»
314     '''
315
316     /**
317      * Generate constructor with argument of given type.
318      */
319     def private Object generateConstructorFromIfc(Type impl) '''
320         «IF (impl instanceof GeneratedType)»
321             «val implType = impl as GeneratedType»
322
323             «IF !(implType.methodDefinitions.empty)»
324                 public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {
325                     «printConstructorPropertySetter(implType)»
326                 }
327             «ENDIF»
328             «FOR implTypeImplement : implType.implements»
329                 «generateConstructorFromIfc(implTypeImplement)»
330             «ENDFOR»
331         «ENDIF»
332     '''
333
334     def private Object printConstructorPropertySetter(Type implementedIfc) '''
335         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
336             «val ifc = implementedIfc as GeneratedType»
337             «FOR getter : ifc.methodDefinitions»
338                 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
339             «ENDFOR»
340             «FOR impl : ifc.implements»
341                 «printConstructorPropertySetter(impl)»
342             «ENDFOR»
343         «ENDIF»
344     '''
345
346     /**
347      * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
348      */
349     def private generateMethodFieldsFrom(Type type) '''
350         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
351             «val ifc = type as GeneratedType»
352             «IF ifc.hasImplementsFromUses»
353                 «val List<Type> done = ifc.getBaseIfcs»
354                 «generateMethodFieldsFromComment(ifc)»
355                 public void fieldsFrom(«DataObject.importedName» arg) {
356                     boolean isValidArg = false;
357                     «FOR impl : ifc.getAllIfcs»
358                         «generateIfCheck(impl, done)»
359                     «ENDFOR»
360                     if (!isValidArg) {
361                         throw new IllegalArgumentException(
362                           "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
363                           "but was: " + arg
364                         );
365                     }
366                 }
367             «ENDIF»
368         «ENDIF»
369     '''
370
371     def private generateMethodFieldsFromComment(GeneratedType type) '''
372         /**
373          *Set fields from given grouping argument. Valid argument is instance of one of following types:
374          * <ul>
375          «FOR impl : type.getAllIfcs»
376          * <li>«impl.fullyQualifiedName»</li>
377          «ENDFOR»
378          * </ul>
379          *
380          * @param arg grouping object
381          * @throws IllegalArgumentException if given argument is none of valid types
382         */
383     '''
384
385     /**
386      * Method is used to find out if given type implements any interface from uses.
387      */
388     def boolean hasImplementsFromUses(GeneratedType type) {
389         var i = 0
390         for (impl : type.getAllIfcs) {
391             if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
392                 i = i + 1
393             }
394         }
395         return i > 0
396     }
397
398     def private generateIfCheck(Type impl, List<Type> done) '''
399         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
400             «val implType = impl as GeneratedType»
401             if (arg instanceof «implType.fullyQualifiedName») {
402                 «printPropertySetter(implType)»
403                 isValidArg = true;
404             }
405         «ENDIF»
406     '''
407
408     def private printPropertySetter(Type implementedIfc) '''
409         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
410         «val ifc = implementedIfc as GeneratedType»
411         «FOR getter : ifc.methodDefinitions»
412             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
413         «ENDFOR»
414         «ENDIF»
415     '''
416
417     private def List<Type> getBaseIfcs(GeneratedType type) {
418         val List<Type> baseIfcs = new ArrayList();
419         for (ifc : type.implements) {
420             if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
421                 baseIfcs.add(ifc)
422             }
423         }
424         return baseIfcs 
425     }
426
427     private def Set<Type> getAllIfcs(Type type) {
428         val Set<Type> baseIfcs = new HashSet()
429         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
430             val ifc = type as GeneratedType
431             for (impl : ifc.implements) {
432                 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
433                     baseIfcs.add(impl)
434                 }
435                 baseIfcs.addAll(impl.getAllIfcs)
436             }
437         }
438         return baseIfcs 
439     }
440
441     private def List<String> toListOfNames(Collection<Type> types) {
442         val List<String> names = new ArrayList
443         for (type : types) {
444             names.add(type.fullyQualifiedName)
445         }
446         return names
447     }
448
449     /**
450      * Template method which generates class attributes.
451      *
452      * @param boolean value which specify whether field is|isn't final
453      * @return string with class attributes and their types
454      */
455     def private generateFields(boolean _final) '''
456         «IF properties !== null»
457             «FOR f : properties»
458                 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
459                 «val restrictions = f.returnType.restrictions»
460                 «IF !_final && restrictions != null»
461                     «IF !(restrictions.lengthConstraints.empty)»
462                         private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_length;
463                     «ENDIF»
464                     «IF !(restrictions.rangeConstraints.empty)»
465                         private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_range;
466                     «ENDIF»
467                 «ENDIF»
468             «ENDFOR»
469         «ENDIF»
470     '''
471
472     def private generateAugmentField(boolean isPrivate) '''
473         «IF augmentField != null»
474             «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
475         «ENDIF»
476     '''
477
478     /**
479      * Template method which generates setter methods
480      *
481      * @return string with the setter methods
482      */
483     def private generateSetters() '''
484         «FOR field : properties SEPARATOR '\n'»
485             «val length = field.fieldName + "_length"»
486             «val range = field.fieldName + "_range"»
487             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
488                 «generateRestrictions(field, "value", length, range)»
489                 this.«field.fieldName» = value;
490                 return this;
491             }
492             «generateLengthMethod(length, field.returnType, type.name+BUILDER, length)»
493             «generateRangeMethod(range, field.returnType.restrictions, field.returnType, type.name+BUILDER, range)»
494         «ENDFOR»
495         «IF augmentField != null»
496
497             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
498                 this.«augmentField.name».put(augmentationType, augmentation);
499                 return this;
500             }
501         «ENDIF»
502     '''
503
504     def generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter, String rangeGetter) '''
505         «val Type type = field.returnType»
506         «IF type instanceof ConcreteType»
507             «createRestrictions(type, paramName, type.name.contains("["), lengthGetter, rangeGetter)»
508         «ELSEIF type instanceof GeneratedTransferObject»
509             «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter, rangeGetter)»
510         «ENDIF»
511     '''
512
513     def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter, String rangeGetter) '''
514         «val restrictions = type.getRestrictions»
515         «IF restrictions !== null»
516             «val boolean isNestedType = !(type instanceof ConcreteType)»
517             «IF !restrictions.lengthConstraints.empty»
518                 «generateLengthRestriction(type, paramName, lengthGetter, isNestedType, isArray)»
519             «ENDIF»
520             «IF !restrictions.rangeConstraints.empty»
521                 «generateRangeRestriction(type, paramName, rangeGetter, isNestedType)»
522             «ENDIF»
523         «ENDIF»
524     '''
525
526     def private generateLengthRestriction(Type type, String paramName, String getterName, boolean isNestedType, boolean isArray) '''
527         «val restrictions = type.getRestrictions»
528         if («paramName» != null) {
529             «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
530             «printLengthConstraint(type, clazz, paramName, isNestedType, isArray)»
531             boolean isValidLength = false;
532             for («Range.importedName»<«clazz.importedNumber»> r : «getterName»()) {
533                 if (r.contains(_constraint)) {
534                     isValidLength = true;
535                 }
536             }
537             if (!isValidLength) {
538                 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «getterName»));
539             }
540         }
541     '''
542
543     def private generateRangeRestriction(Type type, String paramName, String getterName, boolean isNestedType) '''
544         if («paramName» != null) {
545             «printRangeConstraint(type, paramName, isNestedType)»
546             boolean isValidRange = false;
547             for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
548                 if (r.contains(_constraint)) {
549                     isValidRange = true;
550                 }
551             }
552             if (!isValidRange) {
553                 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
554             }
555         }
556     '''
557
558     def private CharSequence generateCopyConstructor(boolean impl) '''
559         «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
560             «val allProps = new ArrayList(properties)»
561             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
562             «val keyType = type.getKey»
563             «IF isList && keyType != null»
564                 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
565                 «Collections.sort(keyProps,
566                     [ p1, p2 |
567                         return p1.name.compareTo(p2.name)
568                     ])
569                 »
570                 «FOR field : keyProps»
571                     «removeProperty(allProps, field.name)»
572                 «ENDFOR»
573                 «removeProperty(allProps, "key")»
574                 if (base.getKey() == null) {
575                     this._key = new «keyType.importedName»(
576                         «FOR keyProp : keyProps SEPARATOR ", "»
577                             base.«keyProp.getterMethodName»()
578                         «ENDFOR»
579                     );
580                     «FOR field : keyProps»
581                         this.«field.fieldName» = base.«field.getterMethodName»();
582                     «ENDFOR»
583                 } else {
584                     this._key = base.getKey();
585                     «FOR field : keyProps»
586                            this.«field.fieldName» = _key.«field.getterMethodName»();
587                     «ENDFOR»
588                 }
589             «ENDIF»
590             «FOR field : allProps»
591                 this.«field.fieldName» = base.«field.getterMethodName»();
592             «ENDFOR»
593             «IF augmentField != null»
594                 «IF !impl»if (base instanceof «type.name»«IMPL») {«ENDIF»
595                     «IF !impl»«type.name»«IMPL» _impl = («type.name»«IMPL») base;«ENDIF»
596                     «val prop = if (impl) "base" else "_impl"»
597                     «IF impl»
598                         switch («prop».«augmentField.name».size()) {
599                         case 0:
600                             this.«augmentField.name» = «Collections.importedName».emptyMap();
601                             break;
602                             case 1:
603                                 final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = «prop».«augmentField.name».entrySet().iterator().next();
604                                 this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());       
605                             break;
606                         default :
607                             this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
608                         }
609                     «ELSE»
610                         this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
611                     «ENDIF»
612                 «IF !impl»}«ENDIF»
613             «ENDIF»
614         }
615     '''
616
617     private def boolean implementsIfc(GeneratedType type, Type impl) {
618         for (Type ifc : type.implements) {
619             if (ifc.equals(impl)) {
620                 return true;
621             }
622         }
623         return false;
624     }
625
626     private def Type getKey(GeneratedType type) {
627         for (m : type.methodDefinitions) {
628             if ("getKey".equals(m.name)) {
629                 return m.returnType;
630             }
631         }
632         return null;
633     }
634
635     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
636         var GeneratedProperty toRemove = null
637         for (p : props) {
638             if (p.name.equals(name)) {
639                 toRemove = p;
640             }
641         }
642         if (toRemove != null) {
643             props.remove(toRemove);
644         }
645     }
646
647     /**
648      * Template method which generate getter methods for IMPL class.
649      * 
650      * @return string with getter methods
651      */
652     def private generateGetters(boolean addOverride) '''
653         «IF !properties.empty»
654             «FOR field : properties SEPARATOR '\n'»
655                 «IF addOverride»@Override«ENDIF»
656                 «field.getterMethod»
657             «ENDFOR»
658         «ENDIF»
659         «IF augmentField != null»
660
661             @SuppressWarnings("unchecked")
662             «IF addOverride»@Override«ENDIF»
663             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
664                 if (augmentationType == null) {
665                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
666                 }
667                 return (E) «augmentField.name».get(augmentationType);
668             }
669         «ENDIF»
670     '''
671
672     /**
673      * Template method which generates the method <code>hashCode()</code>.
674      * 
675      * @return string with the <code>hashCode()</code> method definition in JAVA format
676      */
677     def protected generateHashCode() '''
678         «IF !properties.empty || augmentField != null»
679             @Override
680             public int hashCode() {
681                 final int prime = 31;
682                 int result = 1;
683                 «FOR property : properties»
684                     «IF property.returnType.name.contains("[")»
685                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
686                     «ELSE»
687                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
688                     «ENDIF»
689                 «ENDFOR»
690                 «IF augmentField != null»
691                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
692                 «ENDIF»
693                 return result;
694             }
695         «ENDIF»
696     '''
697
698     /**
699      * Template method which generates the method <code>equals()</code>.
700      * 
701      * @return string with the <code>equals()</code> method definition in JAVA format     
702      */
703     def protected generateEquals() '''
704         «IF !properties.empty || augmentField != null»
705             @Override
706             public boolean equals(«Object.importedName» obj) {
707                 if (this == obj) {
708                     return true;
709                 }
710                 if (!(obj instanceof «DataObject.importedName»)) {
711                     return false;
712                 }
713                 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
714                     return false;
715                 }
716                 «type.importedName» other = («type.importedName»)obj;
717                 «FOR property : properties»
718                     «val fieldName = property.fieldName»
719                     if («fieldName» == null) {
720                         if (other.«property.getterMethodName»() != null) {
721                             return false;
722                         }
723                     «IF property.returnType.name.contains("[")»
724                     } else if(!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
725                     «ELSE»
726                     } else if(!«fieldName».equals(other.«property.getterMethodName»())) {
727                     «ENDIF»
728                         return false;
729                     }
730                 «ENDFOR»
731                 «IF augmentField != null»
732                     if (getClass() == obj.getClass()) {
733                         // Simple case: we are comparing against self
734                         «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
735                         «val fieldName = augmentField.name»
736                         if («fieldName» == null) {
737                             if (otherImpl.«fieldName» != null) {
738                                 return false;
739                             }
740                         } else if(!«fieldName».equals(otherImpl.«fieldName»)) {
741                             return false;
742                         }
743                     } else {
744                         // Hard case: compare our augments with presence there...
745                         for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
746                             if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
747                                 return false;
748                             }
749                         }
750                         // .. and give the other one the chance to do the same
751                         if (!obj.equals(this)) {
752                             return false;
753                         }
754                     }
755                 «ENDIF»
756                 return true;
757             }
758         «ENDIF»
759     '''
760
761     def override generateToString(Collection<GeneratedProperty> properties) '''
762         «IF !(properties === null)»
763             @Override
764             public «String.importedName» toString() {
765                 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
766                 boolean first = true;
767
768                 «FOR property : properties»
769                     if («property.fieldName» != null) {
770                         if (first) {
771                             first = false;
772                         } else {
773                             builder.append(", ");
774                         }
775                         builder.append("«property.fieldName»=");
776                         «IF property.returnType.name.contains("[")»
777                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
778                         «ELSE»
779                             builder.append(«property.fieldName»);
780                         «ENDIF»
781                      }
782                 «ENDFOR»
783                 «IF augmentField != null»
784                     if (first) {
785                         first = false;
786                     } else {
787                         builder.append(", ");
788                     }
789                     builder.append("«augmentField.name»=");
790                     builder.append(«augmentField.name».values());
791                 «ENDIF»
792                 return builder.append(']').toString();
793             }
794         «ENDIF»
795     '''
796
797     override protected getFullyQualifiedName() {
798         '''«type.fullyQualifiedName»Builder'''.toString
799     }
800
801     def implementedInterfaceGetter() '''
802     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
803         return «type.importedName».class;
804     }
805     '''
806     
807     private def createDescription(GeneratedType type) {
808         return '''
809         Class that builds {@link «type.importedName»} instances.
810         
811         @see «type.importedName»
812     '''
813     }
814     
815     override def protected String formatDataForJavaDoc(GeneratedType type) {
816         val typeDescription = createDescription(type)
817
818         return '''
819             «IF !typeDescription.nullOrEmpty»
820             «typeDescription»
821             «ENDIF»
822         '''.toString
823     }
824 }
825