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