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