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