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