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