Merge "BUG-1276: fixed generated union constructor"
[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             «generateCopyConstructor(false)»
217
218             «generateMethodFieldsFrom(type)»
219
220             «generateGetters(false)»
221
222             «generateSetters»
223
224             public «type.name» build() {
225                 return new «type.name»«IMPL»(this);
226             }
227
228             private static final class «type.name»«IMPL» implements «type.name» {
229
230                 «implementedInterfaceGetter»
231
232                 «generateFields(true)»
233
234                 «generateAugmentField(false)»
235
236                 «generateCopyConstructor(true)»
237
238                 «generateGetters(true)»
239
240                 «generateHashCode()»
241
242                 «generateEquals()»
243                 
244                 «generateToString(properties)»
245             }
246
247         }
248     '''
249
250     /**
251      * Generate default constructor and constructor for every implemented interface from uses statements.
252      */
253     def private generateConstructorsFromIfcs(Type type) '''
254         public «type.name»«BUILDER»() {
255         } 
256         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
257             «val ifc = type as GeneratedType»
258             «FOR impl : ifc.implements»
259                 «generateConstructorFromIfc(impl)»
260             «ENDFOR»
261         «ENDIF»
262     '''
263
264     /**
265      * Generate constructor with argument of given type.
266      */
267     def private Object generateConstructorFromIfc(Type impl) '''
268         «IF (impl instanceof GeneratedType)»
269             «val implType = impl as GeneratedType»
270
271             «IF !(implType.methodDefinitions.empty)»
272                 public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {
273                     «printConstructorPropertySetter(implType)»
274                 }
275             «ENDIF»
276             «FOR implTypeImplement : implType.implements»
277                 «generateConstructorFromIfc(implTypeImplement)»
278             «ENDFOR»
279         «ENDIF»
280     '''
281
282     def private Object printConstructorPropertySetter(Type implementedIfc) '''
283         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
284             «val ifc = implementedIfc as GeneratedType»
285             «FOR getter : ifc.methodDefinitions»
286                 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
287             «ENDFOR»
288             «FOR impl : ifc.implements»
289                 «printConstructorPropertySetter(impl)»
290             «ENDFOR»
291         «ENDIF»
292     '''
293
294     /**
295      * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
296      */
297     def private generateMethodFieldsFrom(Type type) '''
298         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
299             «val ifc = type as GeneratedType»
300             «IF ifc.hasImplementsFromUses»
301                 «val List<Type> done = ifc.getBaseIfcs»
302                 «generateMethodFieldsFromComment(ifc)»
303                 public void fieldsFrom(«DataObject.importedName» arg) {
304                     boolean isValidArg = false;
305                     «FOR impl : ifc.getAllIfcs»
306                         «generateIfCheck(impl, done)»
307                     «ENDFOR»
308                     if (!isValidArg) {
309                         throw new IllegalArgumentException(
310                           "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
311                           "but was: " + arg
312                         );
313                     }
314                 }
315             «ENDIF»
316         «ENDIF»
317     '''
318
319     def private generateMethodFieldsFromComment(GeneratedType type) '''
320         /**
321          Set fields from given grouping argument. Valid argument is instance of one of following types:
322          * <ul>
323          «FOR impl : type.getAllIfcs»
324          * <li>«impl.fullyQualifiedName»</li>
325          «ENDFOR»
326          * </ul>
327          *
328          * @param arg grouping object
329          * @throws IllegalArgumentException if given argument is none of valid types
330         */
331     '''
332
333     /**
334      * Method is used to find out if given type implements any interface from uses.
335      */
336     def boolean hasImplementsFromUses(GeneratedType type) {
337         var i = 0
338         for (impl : type.getAllIfcs) {
339             if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
340                 i = i + 1
341             }
342         }
343         return i > 0
344     }
345
346     def private generateIfCheck(Type impl, List<Type> done) '''
347         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
348             «val implType = impl as GeneratedType»
349             if (arg instanceof «implType.fullyQualifiedName») {
350                 «printPropertySetter(implType)»
351                 isValidArg = true;
352             }
353         «ENDIF»
354     '''
355
356     def private printPropertySetter(Type implementedIfc) '''
357         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
358         «val ifc = implementedIfc as GeneratedType»
359         «FOR getter : ifc.methodDefinitions»
360             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
361         «ENDFOR»
362         «ENDIF»
363     '''
364
365     private def List<Type> getBaseIfcs(GeneratedType type) {
366         val List<Type> baseIfcs = new ArrayList();
367         for (ifc : type.implements) {
368             if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
369                 baseIfcs.add(ifc)
370             }
371         }
372         return baseIfcs 
373     }
374
375     private def Set<Type> getAllIfcs(Type type) {
376         val Set<Type> baseIfcs = new HashSet()
377         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
378             val ifc = type as GeneratedType
379             for (impl : ifc.implements) {
380                 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
381                     baseIfcs.add(impl)
382                 }
383                 baseIfcs.addAll(impl.getAllIfcs)
384             }
385         }
386         return baseIfcs 
387     }
388
389     private def List<String> toListOfNames(Collection<Type> types) {
390         val List<String> names = new ArrayList
391         for (type : types) {
392             names.add(type.fullyQualifiedName)
393         }
394         return names
395     }
396
397     /**
398      * Template method which generates class attributes.
399      *
400      * @param boolean value which specify whether field is|isn't final
401      * @return string with class attributes and their types
402      */
403     def private generateFields(boolean _final) '''
404         «IF properties !== null»
405             «FOR f : properties»
406                 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
407                 «val restrictions = f.returnType.restrictions»
408                 «IF !_final && restrictions != null»
409                     «IF !(restrictions.lengthConstraints.empty)»
410                         private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_length;
411                     «ENDIF»
412                     «IF !(restrictions.rangeConstraints.empty)»
413                         private static «List.importedName»<«Range.importedName»<«f.returnType.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.restrictions, 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)»
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) '''
492         if («paramName» != null) {
493             «printRangeConstraint(type, paramName, isNestedType)»
494             boolean isValidRange = false;
495             for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
496                 if (r.contains(_constraint)) {
497                     isValidRange = true;
498                 }
499             }
500             if (!isValidRange) {
501                 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
502             }
503         }
504     '''
505
506     def private CharSequence generateCopyConstructor(boolean impl) '''
507         «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
508             «val allProps = new ArrayList(properties)»
509             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
510             «val keyType = type.getKey»
511             «IF isList && keyType != null»
512                 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
513                 «Collections.sort(keyProps,
514                     [ p1, p2 |
515                         return p1.name.compareTo(p2.name)
516                     ])
517                 »
518                 «FOR field : keyProps»
519                     «removeProperty(allProps, field.name)»
520                 «ENDFOR»
521                 «removeProperty(allProps, "key")»
522                 if (base.getKey() == null) {
523                     this._key = new «keyType.importedName»(
524                         «FOR keyProp : keyProps SEPARATOR ", "»
525                             base.«keyProp.getterMethodName»()
526                         «ENDFOR»
527                     );
528                     «FOR field : keyProps»
529                         this.«field.fieldName» = base.«field.getterMethodName»();
530                     «ENDFOR»
531                 } else {
532                     this._key = base.getKey();
533                     «FOR field : keyProps»
534                            this.«field.fieldName» = _key.«field.getterMethodName»();
535                     «ENDFOR»
536                 }
537             «ENDIF»
538             «FOR field : allProps»
539                 this.«field.fieldName» = base.«field.getterMethodName»();
540             «ENDFOR»
541             «IF augmentField != null»
542                 «IF !impl»if (base instanceof «type.name»«IMPL») {«ENDIF»
543                     «IF !impl»«type.name»«IMPL» _impl = («type.name»«IMPL») base;«ENDIF»
544                     «val prop = if (impl) "base" else "_impl"»
545                     switch («prop».«augmentField.name».size()) {
546                     case 0:
547                         this.«augmentField.name» = «Collections.importedName».emptyMap();
548                         break;
549                     case 1:
550                         final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = «prop».«augmentField.name».entrySet().iterator().next();
551                         this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
552                         break;
553                     default :
554                         this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
555                     }
556                 «IF !impl»}«ENDIF»
557             «ENDIF»
558         }
559     '''
560
561     private def boolean implementsIfc(GeneratedType type, Type impl) {
562         for (Type ifc : type.implements) {
563             if (ifc.equals(impl)) {
564                 return true;
565             }
566         }
567         return false;
568     }
569
570     private def Type getKey(GeneratedType type) {
571         for (m : type.methodDefinitions) {
572             if ("getKey".equals(m.name)) {
573                 return m.returnType;
574             }
575         }
576         return null;
577     }
578
579     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
580         var GeneratedProperty toRemove = null
581         for (p : props) {
582             if (p.name.equals(name)) {
583                 toRemove = p;
584             }
585         }
586         if (toRemove != null) {
587             props.remove(toRemove);
588         }
589     }
590
591     /**
592      * Template method which generate getter methods for IMPL class.
593      * 
594      * @return string with getter methods
595      */
596     def private generateGetters(boolean addOverride) '''
597         «IF !properties.empty»
598             «FOR field : properties SEPARATOR '\n'»
599                 «IF addOverride»@Override«ENDIF»
600                 «field.getterMethod»
601             «ENDFOR»
602         «ENDIF»
603         «IF augmentField != null»
604
605             @SuppressWarnings("unchecked")
606             «IF addOverride»@Override«ENDIF»
607             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
608                 if (augmentationType == null) {
609                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
610                 }
611                 return (E) «augmentField.name».get(augmentationType);
612             }
613         «ENDIF»
614     '''
615
616     /**
617      * Template method which generates the method <code>hashCode()</code>.
618      * 
619      * @return string with the <code>hashCode()</code> method definition in JAVA format
620      */
621     def protected generateHashCode() '''
622         «IF !properties.empty || augmentField != null»
623             @Override
624             public int hashCode() {
625                 final int prime = 31;
626                 int result = 1;
627                 «FOR property : properties»
628                     «IF property.returnType.name.contains("[")»
629                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
630                     «ELSE»
631                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
632                     «ENDIF»
633                 «ENDFOR»
634                 «IF augmentField != null»
635                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
636                 «ENDIF»
637                 return result;
638             }
639         «ENDIF»
640     '''
641
642     /**
643      * Template method which generates the method <code>equals()</code>.
644      * 
645      * @return string with the <code>equals()</code> method definition in JAVA format     
646      */
647     def protected generateEquals() '''
648         «IF !properties.empty || augmentField != null»
649             @Override
650             public boolean equals(«Object.importedName» obj) {
651                 if (this == obj) {
652                     return true;
653                 }
654                 if (obj == null) {
655                     return false;
656                 }
657                 if (getClass() != obj.getClass()) {
658                     return false;
659                 }
660                 «type.name»«IMPL» other = («type.name»«IMPL») obj;
661                 «FOR property : properties»
662                     «val fieldName = property.fieldName»
663                     if («fieldName» == null) {
664                         if (other.«fieldName» != null) {
665                             return false;
666                         }
667                     «IF property.returnType.name.contains("[")»
668                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
669                     «ELSE»
670                     } else if(!«fieldName».equals(other.«fieldName»)) {
671                     «ENDIF»
672                         return false;
673                     }
674                 «ENDFOR»
675                 «IF augmentField != null»
676                     «val fieldName = augmentField.name»
677                     if («fieldName» == null) {
678                         if (other.«fieldName» != null) {
679                             return false;
680                         }
681                     } else if(!«fieldName».equals(other.«fieldName»)) {
682                         return false;
683                     }
684                 «ENDIF»
685                 return true;
686             }
687         «ENDIF»
688     '''
689
690     def override generateToString(Collection<GeneratedProperty> properties) '''
691         «IF !(properties === null)»
692             @Override
693             public «String.importedName» toString() {
694                 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
695                 boolean first = true;
696
697                 «FOR property : properties»
698                     if («property.fieldName» != null) {
699                         if (first) {
700                             first = false;
701                         } else {
702                             builder.append(", ");
703                         }
704                         builder.append("«property.fieldName»=");
705                         «IF property.returnType.name.contains("[")»
706                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
707                         «ELSE»
708                             builder.append(«property.fieldName»);
709                         «ENDIF»
710                      }
711                 «ENDFOR»
712                 «IF augmentField != null»
713                     if (first) {
714                         first = false;
715                     } else {
716                         builder.append(", ");
717                     }
718                     builder.append("«augmentField.name»=");
719                     builder.append(«augmentField.name».values());
720                 «ENDIF»
721                 return builder.append(']').toString();
722             }
723         «ENDIF»
724     '''
725
726     override protected getFullyQualifiedName() {
727         '''«type.fullyQualifiedName»Builder'''.toString
728     }
729
730     def implementedInterfaceGetter() '''
731     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
732         return «type.importedName».class;
733     }
734     '''
735
736 }
737