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