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