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