Integrate JavaTypeName as Identifier
[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).toLowerCase»
422                 public static final «List.importedName»<String> «c.getName» = «ImmutableList.importedName».of(
423                 «FOR v : cValue.keySet SEPARATOR ", "»"«v.escapeJava»"«ENDFOR»);
424                 «IF cValue.size == 1»
425                    private static final «Pattern.importedName» «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «Pattern.importedName».compile(«c.getName».get(0));
426                    private static final String «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = "«cValue.values.get(0).escapeJava»";
427                 «ELSE»
428                    private static final «Pattern.importedName»[] «Constants.MEMBER_PATTERN_LIST»«fieldSuffix» = «CodeHelpers.importedName».compilePatterns(«c.getName»);
429                    private static final String[] «Constants.MEMBER_REGEX_LIST»«fieldSuffix» = { «
430                    FOR v : cValue.values SEPARATOR ", "»"«v.escapeJava»"«ENDFOR» };
431                 «ENDIF»
432             «ELSE»
433                 «emitConstant(c)»
434             «ENDIF»
435         «ENDFOR»
436     '''
437
438     def private generateCheckers(GeneratedProperty field, Restrictions restrictions, Type actualType) '''
439        «IF restrictions.rangeConstraint.present»
440            «AbstractRangeGenerator.forType(actualType).generateRangeChecker(field.name.toFirstUpper,
441                restrictions.rangeConstraint.get, this)»
442        «ENDIF»
443        «IF restrictions.lengthConstraint.present»
444            «LengthGenerator.generateLengthChecker(field.fieldName.toString, actualType, restrictions.lengthConstraint.get, this)»
445        «ENDIF»
446     '''
447
448     def private checkArgument(GeneratedProperty property, Restrictions restrictions, Type actualType) '''
449        «IF restrictions.getRangeConstraint.isPresent»
450            «IF actualType instanceof ConcreteType»
451                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value")»
452            «ELSE»
453                «AbstractRangeGenerator.forType(actualType).generateRangeCheckerCall(property.getName.toFirstUpper, "value.getValue()")»
454            «ENDIF»
455        «ENDIF»
456        «IF restrictions.getLengthConstraint.isPresent»
457            «IF actualType instanceof ConcreteType»
458                «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value")»
459            «ELSE»
460                «LengthGenerator.generateLengthCheckerCall(property.fieldName.toString, "value.getValue()")»
461            «ENDIF»
462        «ENDIF»
463
464        «val fieldUpperCase = property.fieldName.toString.toUpperCase()»
465        «FOR currentConstant : type.getConstantDefinitions»
466            «IF currentConstant.getName.startsWith(TypeConstants.PATTERN_CONSTANT_NAME)
467                && fieldUpperCase.equals(currentConstant.getName.substring(TypeConstants.PATTERN_CONSTANT_NAME.length))»
468            «CodeHelpers.importedName».checkPattern(value, «Constants.MEMBER_PATTERN_LIST»«property.fieldName», «Constants.MEMBER_REGEX_LIST»«property.fieldName»);
469            «ENDIF»
470        «ENDFOR»
471     '''
472
473     def private Restrictions restrictionsForSetter(Type actualType) {
474         if (actualType instanceof GeneratedType) {
475             return null;
476         }
477         return actualType.restrictions;
478     }
479
480     def private generateListSetter(GeneratedProperty field, Type actualType) '''
481         «val restrictions = restrictionsForSetter(actualType)»
482         «IF restrictions !== null»
483             «generateCheckers(field, restrictions, actualType)»
484         «ENDIF»
485         public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» values) {
486         «IF restrictions !== null»
487             if (values != null) {
488                for («actualType.getFullyQualifiedName» value : values) {
489                    «checkArgument(field, restrictions, actualType)»
490                }
491             }
492         «ENDIF»
493             this.«field.fieldName.toString» = values;
494             return this;
495         }
496
497     '''
498
499     def private generateSetter(GeneratedProperty field, Type actualType) '''
500         «val restrictions = restrictionsForSetter(actualType)»
501         «IF restrictions !== null»
502             «generateCheckers(field, restrictions, actualType)»
503         «ENDIF»
504
505         public «type.getName»Builder set«field.getName.toFirstUpper»(final «field.returnType.importedName» value) {
506         «IF restrictions !== null»
507             if (value != null) {
508                 «checkArgument(field, restrictions, actualType)»
509             }
510         «ENDIF»
511             this.«field.fieldName.toString» = value;
512             return this;
513         }
514     '''
515
516     private def Type getActualType(ParameterizedType ptype) {
517         return ptype.getActualTypeArguments.get(0)
518     }
519
520     /**
521      * Template method which generates setter methods
522      *
523      * @return string with the setter methods
524      */
525     def private generateSetters() '''
526         «FOR property : properties»
527             «IF property.returnType instanceof ParameterizedType
528                     && (property.returnType as ParameterizedType).rawType.equals(Types.LIST_TYPE)»
529                 «generateListSetter(property, getActualType(property.returnType as ParameterizedType))»
530             «ELSE»
531                 «generateSetter(property, property.returnType)»
532             «ENDIF»
533         «ENDFOR»
534
535         «IF augmentField !== null»
536             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentationValue) {
537                 if (augmentationValue == null) {
538                     return remove«augmentField.name.toFirstUpper»(augmentationType);
539                 }
540
541                 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
542                     this.«augmentField.name» = new «HashMap.importedName»<>();
543                 }
544
545                 this.«augmentField.name».put(augmentationType, augmentationValue);
546                 return this;
547             }
548
549             public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
550                 if (this.«augmentField.name» instanceof «HashMap.importedName») {
551                     this.«augmentField.name».remove(augmentationType);
552                 }
553                 return this;
554             }
555         «ENDIF»
556     '''
557
558     def private CharSequence generateCopyConstructor(boolean impl) '''
559         «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
560             «val allProps = new ArrayList(properties)»
561             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
562             «val keyType = type.getKey»
563             «IF isList && keyType !== null»
564                 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
565                 «Collections.sort(keyProps,
566                     [ p1, p2 |
567                         return p1.name.compareTo(p2.name)
568                     ])
569                 »
570                 «FOR field : keyProps»
571                     «removeProperty(allProps, field.name)»
572                 «ENDFOR»
573                 «removeProperty(allProps, "key")»
574                 if (base.getKey() == null) {
575                     this._key = new «keyType.importedName»(
576                         «FOR keyProp : keyProps SEPARATOR ", "»
577                             base.«keyProp.getterMethodName»()
578                         «ENDFOR»
579                     );
580                     «FOR field : keyProps»
581                         this.«field.fieldName» = base.«field.getterMethodName»();
582                     «ENDFOR»
583                 } else {
584                     this._key = base.getKey();
585                     «FOR field : keyProps»
586                            this.«field.fieldName» = _key.«field.getterMethodName»();
587                     «ENDFOR»
588                 }
589             «ENDIF»
590             «FOR field : allProps»
591                 this.«field.fieldName» = base.«field.getterMethodName»();
592             «ENDFOR»
593             «IF augmentField !== null»
594                 «IF impl»
595                     this.«augmentField.name» = «ImmutableMap.importedName».copyOf(base.«augmentField.name»);
596                 «ELSE»
597                     if (base instanceof «type.name»«IMPL») {
598                         «type.name»«IMPL» impl = («type.name»«IMPL») base;
599                         if (!impl.«augmentField.name».isEmpty()) {
600                             this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
601                         }
602                     } else if (base instanceof «AugmentationHolder.importedName») {
603                         @SuppressWarnings("unchecked")
604                         «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
605                         if (!casted.augmentations().isEmpty()) {
606                             this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
607                         }
608                     }
609                 «ENDIF»
610             «ENDIF»
611         }
612     '''
613
614     private def boolean implementsIfc(GeneratedType type, Type impl) {
615         for (Type ifc : type.implements) {
616             if (ifc.equals(impl)) {
617                 return true;
618             }
619         }
620         return false;
621     }
622
623     private def Type getKey(GeneratedType type) {
624         for (m : type.methodDefinitions) {
625             if ("getKey".equals(m.name)) {
626                 return m.returnType;
627             }
628         }
629         return null;
630     }
631
632     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
633         var GeneratedProperty toRemove = null
634         for (p : props) {
635             if (p.name.equals(name)) {
636                 toRemove = p;
637             }
638         }
639         if (toRemove !== null) {
640             props.remove(toRemove);
641         }
642     }
643
644     /**
645      * Template method which generate getter methods for IMPL class.
646      *
647      * @return string with getter methods
648      */
649     def private generateGetters(boolean addOverride) '''
650         «IF !properties.empty»
651             «FOR field : properties SEPARATOR '\n'»
652                 «IF addOverride»@Override«ENDIF»
653                 «field.getterMethod»
654             «ENDFOR»
655         «ENDIF»
656         «IF augmentField !== null»
657
658             @SuppressWarnings("unchecked")
659             «IF addOverride»@Override«ENDIF»
660             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
661                 return (E) «augmentField.name».get(«CodeHelpers.importedName».nonNullValue(augmentationType, "augmentationType"));
662             }
663         «ENDIF»
664     '''
665
666     /**
667      * Template method which generates the method <code>hashCode()</code>.
668      *
669      * @return string with the <code>hashCode()</code> method definition in JAVA format
670      */
671     def protected generateHashCode() '''
672         «IF !properties.empty || augmentField !== null»
673             private int hash = 0;
674             private volatile boolean hashValid = false;
675
676             @Override
677             public int hashCode() {
678                 if (hashValid) {
679                     return hash;
680                 }
681
682                 final int prime = 31;
683                 int result = 1;
684                 «FOR property : properties»
685                     «IF property.returnType.name.contains("[")»
686                     result = prime * result + «Arrays.importedName».hashCode(«property.fieldName»);
687                     «ELSE»
688                     result = prime * result + «Objects.importedName».hashCode(«property.fieldName»);
689                     «ENDIF»
690                 «ENDFOR»
691                 «IF augmentField !== null»
692                     result = prime * result + «Objects.importedName».hashCode(«augmentField.name»);
693                 «ENDIF»
694
695                 hash = result;
696                 hashValid = true;
697                 return result;
698             }
699         «ENDIF»
700     '''
701
702     /**
703      * Template method which generates the method <code>equals()</code>.
704      *
705      * @return string with the <code>equals()</code> method definition in JAVA format
706      */
707     def protected generateEquals() '''
708         «IF !properties.empty || augmentField !== null»
709             @Override
710             public boolean equals(«Object.importedName» obj) {
711                 if (this == obj) {
712                     return true;
713                 }
714                 if (!(obj instanceof «DataObject.importedName»)) {
715                     return false;
716                 }
717                 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
718                     return false;
719                 }
720                 «type.importedName» other = («type.importedName»)obj;
721                 «FOR property : properties»
722                     «val fieldName = property.fieldName»
723                     «IF property.returnType.name.contains("[")»
724                     if (!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
725                     «ELSE»
726                     if (!«Objects.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
727                     «ENDIF»
728                         return false;
729                     }
730                 «ENDFOR»
731                 «IF augmentField !== null»
732                     if (getClass() == obj.getClass()) {
733                         // Simple case: we are comparing against self
734                         «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
735                         «val fieldName = augmentField.name»
736                         if (!«Objects.importedName».equals(«fieldName», otherImpl.«fieldName»)) {
737                             return false;
738                         }
739                     } else {
740                         // Hard case: compare our augments with presence there...
741                         for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
742                             if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
743                                 return false;
744                             }
745                         }
746                         // .. and give the other one the chance to do the same
747                         if (!obj.equals(this)) {
748                             return false;
749                         }
750                     }
751                 «ENDIF»
752                 return true;
753             }
754         «ENDIF»
755     '''
756
757     def override generateToString(Collection<GeneratedProperty> properties) '''
758         «IF properties !== null»
759             @Override
760             public «String.importedName» toString() {
761                 final «MoreObjects.importedName».ToStringHelper helper = «MoreObjects.importedName».toStringHelper("«type.name»");
762                 «FOR property : properties»
763                     «CodeHelpers.importedName».appendValue(helper, "«property.fieldName»", «property.fieldName»);
764                 «ENDFOR»
765                 «IF augmentField !== null»
766                     «CodeHelpers.importedName».appendValue(helper, "«augmentField.name»", «augmentField.name».values());
767                 «ENDIF»
768                 return helper.toString();
769             }
770         «ENDIF»
771     '''
772
773     def implementedInterfaceGetter() '''
774     @Override
775     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
776         return «type.importedName».class;
777     }
778     '''
779
780     private def createDescription(GeneratedType type) {
781         return '''
782         Class that builds {@link «type.importedName»} instances.
783
784         @see «type.importedName»
785     '''
786     }
787
788     override def protected String formatDataForJavaDoc(GeneratedType type) {
789         val typeDescription = createDescription(type)
790
791         return '''
792             «IF !typeDescription.nullOrEmpty»
793             «typeDescription»
794             «ENDIF»
795         '''.toString
796     }
797 }
798