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