BUG-1407 fix.
[mdsal.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BuilderTemplate.xtend
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.sal.java.api.generator
9
10 import 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         «wrapToDocumentation(formatDataForJavaDoc(type))»
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                     «IF impl»
546                         switch («prop».«augmentField.name».size()) {
547                         case 0:
548                             this.«augmentField.name» = «Collections.importedName».emptyMap();
549                             break;
550                             case 1:
551                                 final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = «prop».«augmentField.name».entrySet().iterator().next();
552                                 this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());       
553                             break;
554                         default :
555                             this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
556                         }
557                     «ELSE»
558                         this.«augmentField.name» = new «HashMap.importedName»<>(«prop».«augmentField.name»);
559                     «ENDIF»
560                 «IF !impl»}«ENDIF»
561             «ENDIF»
562         }
563     '''
564
565     private def boolean implementsIfc(GeneratedType type, Type impl) {
566         for (Type ifc : type.implements) {
567             if (ifc.equals(impl)) {
568                 return true;
569             }
570         }
571         return false;
572     }
573
574     private def Type getKey(GeneratedType type) {
575         for (m : type.methodDefinitions) {
576             if ("getKey".equals(m.name)) {
577                 return m.returnType;
578             }
579         }
580         return null;
581     }
582
583     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
584         var GeneratedProperty toRemove = null
585         for (p : props) {
586             if (p.name.equals(name)) {
587                 toRemove = p;
588             }
589         }
590         if (toRemove != null) {
591             props.remove(toRemove);
592         }
593     }
594
595     /**
596      * Template method which generate getter methods for IMPL class.
597      * 
598      * @return string with getter methods
599      */
600     def private generateGetters(boolean addOverride) '''
601         «IF !properties.empty»
602             «FOR field : properties SEPARATOR '\n'»
603                 «IF addOverride»@Override«ENDIF»
604                 «field.getterMethod»
605             «ENDFOR»
606         «ENDIF»
607         «IF augmentField != null»
608
609             @SuppressWarnings("unchecked")
610             «IF addOverride»@Override«ENDIF»
611             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
612                 if (augmentationType == null) {
613                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
614                 }
615                 return (E) «augmentField.name».get(augmentationType);
616             }
617         «ENDIF»
618     '''
619
620     /**
621      * Template method which generates the method <code>hashCode()</code>.
622      * 
623      * @return string with the <code>hashCode()</code> method definition in JAVA format
624      */
625     def protected generateHashCode() '''
626         «IF !properties.empty || augmentField != null»
627             @Override
628             public int hashCode() {
629                 final int prime = 31;
630                 int result = 1;
631                 «FOR property : properties»
632                     «IF property.returnType.name.contains("[")»
633                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
634                     «ELSE»
635                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
636                     «ENDIF»
637                 «ENDFOR»
638                 «IF augmentField != null»
639                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
640                 «ENDIF»
641                 return result;
642             }
643         «ENDIF»
644     '''
645
646     /**
647      * Template method which generates the method <code>equals()</code>.
648      * 
649      * @return string with the <code>equals()</code> method definition in JAVA format     
650      */
651     def protected generateEquals() '''
652         «IF !properties.empty || augmentField != null»
653             @Override
654             public boolean equals(«Object.importedName» obj) {
655                 if (this == obj) {
656                     return true;
657                 }
658                 if (obj == null) {
659                     return false;
660                 }
661                 if (getClass() != obj.getClass()) {
662                     return false;
663                 }
664                 «type.name»«IMPL» other = («type.name»«IMPL») obj;
665                 «FOR property : properties»
666                     «val fieldName = property.fieldName»
667                     if («fieldName» == null) {
668                         if (other.«fieldName» != null) {
669                             return false;
670                         }
671                     «IF property.returnType.name.contains("[")»
672                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {
673                     «ELSE»
674                     } else if(!«fieldName».equals(other.«fieldName»)) {
675                     «ENDIF»
676                         return false;
677                     }
678                 «ENDFOR»
679                 «IF augmentField != null»
680                     «val fieldName = augmentField.name»
681                     if («fieldName» == null) {
682                         if (other.«fieldName» != null) {
683                             return false;
684                         }
685                     } else if(!«fieldName».equals(other.«fieldName»)) {
686                         return false;
687                     }
688                 «ENDIF»
689                 return true;
690             }
691         «ENDIF»
692     '''
693
694     def override generateToString(Collection<GeneratedProperty> properties) '''
695         «IF !(properties === null)»
696             @Override
697             public «String.importedName» toString() {
698                 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
699                 boolean first = true;
700
701                 «FOR property : properties»
702                     if («property.fieldName» != null) {
703                         if (first) {
704                             first = false;
705                         } else {
706                             builder.append(", ");
707                         }
708                         builder.append("«property.fieldName»=");
709                         «IF property.returnType.name.contains("[")»
710                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
711                         «ELSE»
712                             builder.append(«property.fieldName»);
713                         «ENDIF»
714                      }
715                 «ENDFOR»
716                 «IF augmentField != null»
717                     if (first) {
718                         first = false;
719                     } else {
720                         builder.append(", ");
721                     }
722                     builder.append("«augmentField.name»=");
723                     builder.append(«augmentField.name».values());
724                 «ENDIF»
725                 return builder.append(']').toString();
726             }
727         «ENDIF»
728     '''
729
730     override protected getFullyQualifiedName() {
731         '''«type.fullyQualifiedName»Builder'''.toString
732     }
733
734     def implementedInterfaceGetter() '''
735     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
736         return «type.importedName».class;
737     }
738     '''
739
740 }
741