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