710f1022104491baf8853ad89d4a2806be45a2eb
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / 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.yangtools.sal.java.api.generator
9
10 import com.google.common.collect.ImmutableSortedSet
11 import com.google.common.collect.Range
12 import java.util.ArrayList
13 import java.util.Arrays
14 import java.util.Collection
15 import java.util.Collections
16 import java.util.HashMap
17 import java.util.HashSet
18 import java.util.LinkedHashSet
19 import java.util.List
20 import java.util.Map
21 import java.util.Set
22 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
23 import org.opendaylight.yangtools.binding.generator.util.Types
24 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
25 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType
26 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty
27 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject
28 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType
29 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature
30 import org.opendaylight.yangtools.sal.binding.model.api.Type
31 import org.opendaylight.yangtools.yang.binding.Augmentable
32 import org.opendaylight.yangtools.yang.binding.DataObject
33 import org.opendaylight.yangtools.yang.binding.Identifiable
34 import org.opendaylight.yangtools.concepts.Builder
35 import org.opendaylight.yangtools.yang.binding.AugmentationHolder
36
37 /**
38  * Template for generating JAVA builder classes.
39  */
40
41 class BuilderTemplate extends BaseTemplate {
42
43     /**
44      * Constant with the name of the concrete method.
45      */
46     val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"
47
48     /**
49      * Constant with the suffix for builder classes.
50      */
51     val static BUILDER = 'Builder'
52
53     /**
54      * Constant with the name of the BuilderFor interface
55      */
56      val static BUILDERFOR = Builder.simpleName;
57
58     /**
59      * Constant with suffix for the classes which are generated from the builder classes.
60      */
61     val static IMPL = 'Impl'
62
63     /**
64      * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
65      */
66     var GeneratedProperty augmentField
67
68     /**
69      * Set of class attributes (fields) which are derived from the getter methods names
70      */
71     val Set<GeneratedProperty> properties
72
73     private static val METHOD_COMPARATOR = new AlphabeticallyTypeMemberComparator<MethodSignature>();
74
75     /**
76      * Constructs new instance of this class.
77      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>
78      */
79     new(GeneratedType genType) {
80         super(genType)
81         this.properties = propertiesFromMethods(createMethods)
82         importMap.put(Builder.simpleName, Builder.package.name)
83     }
84
85     /**
86      * Returns set of method signature instances which contains all the methods of the <code>genType</code>
87      * and all the methods of the implemented interfaces.
88      *
89      * @returns set of method signature instances
90      */
91     def private Set<MethodSignature> createMethods() {
92         val Set<MethodSignature> methods = new LinkedHashSet();
93         methods.addAll(type.methodDefinitions)
94         collectImplementedMethods(methods, type.implements)
95         val Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods).build()
96
97         return sortedMethods
98     }
99
100     /**
101      * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
102      * and recursively their implemented interfaces.
103      *
104      * @param methods set of method signatures
105      * @param implementedIfcs list of implemented interfaces
106      */
107     def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {
108         if (implementedIfcs == null || implementedIfcs.empty) {
109             return
110         }
111         for (implementedIfc : implementedIfcs) {
112             if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
113                 val ifc = implementedIfc as GeneratedType
114                 methods.addAll(ifc.methodDefinitions)
115                 collectImplementedMethods(methods, ifc.implements)
116             } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {
117                 for (m : Augmentable.methods) {
118                     if (m.name == GET_AUGMENTATION_METHOD_NAME) {
119                         val fullyQualifiedName = m.returnType.name
120                         val pkg = fullyQualifiedName.package
121                         val name = fullyQualifiedName.name
122                         val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)
123                         val refType = new ReferencedTypeImpl(pkg, name)
124                         val generic = new ReferencedTypeImpl(type.packageName, type.name)
125                         val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)
126                         tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)
127                         augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter
128                     }
129                 }
130             }
131         }
132     }
133
134     /**
135      * Returns the first element of the list <code>elements</code>.
136      *
137      * @param list of elements
138      */
139     def private <E> first(List<E> elements) {
140         elements.get(0)
141     }
142
143     /**
144      * Returns the name of the package from <code>fullyQualifiedName</code>.
145      *
146      * @param fullyQualifiedName string with fully qualified type name (package + type)
147      * @return string with the package name
148      */
149     def private String getPackage(String fullyQualifiedName) {
150         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
151         return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)
152     }
153
154     /**
155      * Returns the name of tye type from <code>fullyQualifiedName</code>
156      *
157      * @param fullyQualifiedName string with fully qualified type name (package + type)
158      * @return string with the name of the type
159      */
160     def private String getName(String fullyQualifiedName) {
161         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)
162         return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)
163     }
164
165     /**
166      * Creates set of generated property instances from getter <code>methods</code>.
167      *
168      * @param set of method signature instances which should be transformed to list of properties
169      * @return set of generated property instances which represents the getter <code>methods</code>
170      */
171     def private propertiesFromMethods(Collection<MethodSignature> methods) {
172         if (methods == null || methods.isEmpty()) {
173             return Collections.emptySet
174         }
175         val Set<GeneratedProperty> result = new LinkedHashSet
176         for (m : methods) {
177             val createdField = m.propertyFromGetter
178             if (createdField != null) {
179                 result.add(createdField)
180             }
181         }
182         return result
183     }
184
185     /**
186      * Creates generated property instance from the getter <code>method</code> name and return type.
187      *
188      * @param method method signature from which is the method name and return type obtained
189      * @return generated property instance for the getter <code>method</code>
190      * @throws IllegalArgumentException<ul>
191      *  <li>if the <code>method</code> equals <code>null</code></li>
192      *  <li>if the name of the <code>method</code> equals <code>null</code></li>
193      *  <li>if the name of the <code>method</code> is empty</li>
194      *  <li>if the return type of the <code>method</code> equals <code>null</code></li>
195      * </ul>
196      */
197     def private GeneratedProperty propertyFromGetter(MethodSignature method) {
198         if (method == null || method.name == null || method.name.empty || method.returnType == null) {
199             throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")
200         }
201         var prefix = "get";
202         if(Types.BOOLEAN.equals(method.returnType)) {
203             prefix = "is";
204         }
205         if (method.name.startsWith(prefix)) {
206             val fieldName = method.getName().substring(prefix.length()).toFirstLower
207             val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")
208             tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)
209             return tmpGenTO.toInstance.properties.first
210         }
211     }
212
213     /**
214      * Template method which generates JAVA class body for builder class and for IMPL class.
215      *
216      * @return string with JAVA source code
217      */
218     override body() '''
219         «wrapToDocumentation(formatDataForJavaDoc(type))»
220         public class «type.name»«BUILDER» implements «BUILDERFOR» <«type.importedName»> {
221
222             «generateFields(false)»
223
224             «generateAugmentField(false)»
225
226             «generateConstructorsFromIfcs(type)»
227
228             «generateCopyConstructor(false)»
229
230             «generateMethodFieldsFrom(type)»
231
232             «generateGetters(false)»
233
234             «generateSetters»
235
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                     if (!isValidArg) {
319                         throw new IllegalArgumentException(
320                           "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
321                           "but was: " + arg
322                         );
323                     }
324                 }
325             «ENDIF»
326         «ENDIF»
327     '''
328
329     def private generateMethodFieldsFromComment(GeneratedType type) '''
330         /**
331          *Set fields from given grouping argument. Valid argument is instance of one of following types:
332          * <ul>
333          «FOR impl : type.getAllIfcs»
334          * <li>«impl.fullyQualifiedName»</li>
335          «ENDFOR»
336          * </ul>
337          *
338          * @param arg grouping object
339          * @throws IllegalArgumentException if given argument is none of valid types
340         */
341     '''
342
343     /**
344      * Method is used to find out if given type implements any interface from uses.
345      */
346     def boolean hasImplementsFromUses(GeneratedType type) {
347         var i = 0
348         for (impl : type.getAllIfcs) {
349             if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
350                 i = i + 1
351             }
352         }
353         return i > 0
354     }
355
356     def private generateIfCheck(Type impl, List<Type> done) '''
357         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
358             «val implType = impl as GeneratedType»
359             if (arg instanceof «implType.fullyQualifiedName») {
360                 «printPropertySetter(implType)»
361                 isValidArg = true;
362             }
363         «ENDIF»
364     '''
365
366     def private printPropertySetter(Type implementedIfc) '''
367         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
368         «val ifc = implementedIfc as GeneratedType»
369         «FOR getter : ifc.methodDefinitions»
370             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
371         «ENDFOR»
372         «ENDIF»
373     '''
374
375     private def List<Type> getBaseIfcs(GeneratedType type) {
376         val List<Type> baseIfcs = new ArrayList();
377         for (ifc : type.implements) {
378             if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
379                 baseIfcs.add(ifc)
380             }
381         }
382         return baseIfcs
383     }
384
385     private def Set<Type> getAllIfcs(Type type) {
386         val Set<Type> baseIfcs = new HashSet()
387         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
388             val ifc = type as GeneratedType
389             for (impl : ifc.implements) {
390                 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
391                     baseIfcs.add(impl)
392                 }
393                 baseIfcs.addAll(impl.getAllIfcs)
394             }
395         }
396         return baseIfcs
397     }
398
399     private def List<String> toListOfNames(Collection<Type> types) {
400         val List<String> names = new ArrayList
401         for (type : types) {
402             names.add(type.fullyQualifiedName)
403         }
404         return names
405     }
406
407     /**
408      * Template method which generates class attributes.
409      *
410      * @param boolean value which specify whether field is|isn't final
411      * @return string with class attributes and their types
412      */
413     def private generateFields(boolean _final) '''
414         «IF properties !== null»
415             «FOR f : properties»
416                 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
417                 «val restrictions = f.returnType.restrictions»
418                 «IF !_final && restrictions != null»
419                     «IF !(restrictions.lengthConstraints.empty)»
420                         private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_length;
421                     «ENDIF»
422                     «IF !(restrictions.rangeConstraints.empty)»
423                         private static «List.importedName»<«Range.importedName»<«f.returnType.importedNumber»>> «f.fieldName»_range;
424                     «ENDIF»
425                 «ENDIF»
426             «ENDFOR»
427         «ENDIF»
428     '''
429
430     def private generateAugmentField(boolean isPrivate) '''
431         «IF augmentField != null»
432             «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();
433         «ENDIF»
434     '''
435
436     /**
437      * Template method which generates setter methods
438      *
439      * @return string with the setter methods
440      */
441     def private generateSetters() '''
442         «FOR field : properties SEPARATOR '\n'»
443             «val length = field.fieldName + "_length"»
444             «val range = field.fieldName + "_range"»
445             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
446                 «generateRestrictions(field, "value", length, range)»
447                 this.«field.fieldName» = value;
448                 return this;
449             }
450             «generateLengthMethod(length, field.returnType, type.name+BUILDER, length)»
451             «generateRangeMethod(range, field.returnType.restrictions, field.returnType, type.name+BUILDER, range)»
452         «ENDFOR»
453         «IF augmentField != null»
454
455             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
456                 if (augmentation == null) {
457                     return remove«augmentField.name.toFirstUpper»(augmentationType);
458                 }
459                 this.«augmentField.name».put(augmentationType, augmentation);
460                 return this;
461             }
462
463             public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
464                 this.«augmentField.name».remove(augmentationType);
465                 return this;
466             }
467         «ENDIF»
468     '''
469
470     def generateRestrictions(GeneratedProperty field, String paramName, String lengthGetter, String rangeGetter) '''
471         «val Type type = field.returnType»
472         «IF type instanceof ConcreteType»
473             «createRestrictions(type, paramName, type.name.contains("["), lengthGetter, rangeGetter)»
474         «ELSEIF type instanceof GeneratedTransferObject»
475             «createRestrictions(type, paramName, isArrayType(type as GeneratedTransferObject), lengthGetter, rangeGetter)»
476         «ENDIF»
477     '''
478
479     def private createRestrictions(Type type, String paramName, boolean isArray, String lengthGetter, String rangeGetter) '''
480         «val restrictions = type.getRestrictions»
481         «IF restrictions !== null»
482             «val boolean isNestedType = !(type instanceof ConcreteType)»
483             «IF !restrictions.lengthConstraints.empty»
484                 «generateLengthRestriction(type, paramName, lengthGetter, isNestedType, isArray)»
485             «ENDIF»
486             «IF !restrictions.rangeConstraints.empty»
487                 «generateRangeRestriction(type, paramName, rangeGetter, isNestedType)»
488             «ENDIF»
489         «ENDIF»
490     '''
491
492     def private generateLengthRestriction(Type type, String paramName, String getterName, boolean isNestedType, boolean isArray) '''
493         «val restrictions = type.getRestrictions»
494         if («paramName» != null) {
495             «val clazz = restrictions.lengthConstraints.iterator.next.min.class»
496             «printLengthConstraint(type, clazz, paramName, isNestedType, isArray)»
497             boolean isValidLength = false;
498             for («Range.importedName»<«clazz.importedNumber»> r : «getterName»()) {
499                 if (r.contains(_constraint)) {
500                     isValidLength = true;
501                 }
502             }
503             if (!isValidLength) {
504                 throw new IllegalArgumentException(String.format("Invalid length: %s, expected: %s.", «paramName», «getterName»));
505             }
506         }
507     '''
508
509     def private generateRangeRestriction(Type type, String paramName, String getterName, boolean isNestedType) '''
510         if («paramName» != null) {
511             «printRangeConstraint(type, paramName, isNestedType)»
512             boolean isValidRange = false;
513             for («Range.importedName»<«type.importedNumber»> r : «getterName»()) {
514                 if (r.contains(_constraint)) {
515                     isValidRange = true;
516                 }
517             }
518             if (!isValidRange) {
519                 throw new IllegalArgumentException(String.format("Invalid range: %s, expected: %s.", «paramName», «getterName»));
520             }
521         }
522     '''
523
524     def private CharSequence generateCopyConstructor(boolean impl) '''
525         «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
526             «val allProps = new ArrayList(properties)»
527             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
528             «val keyType = type.getKey»
529             «IF isList && keyType != null»
530                 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
531                 «Collections.sort(keyProps,
532                     [ p1, p2 |
533                         return p1.name.compareTo(p2.name)
534                     ])
535                 »
536                 «FOR field : keyProps»
537                     «removeProperty(allProps, field.name)»
538                 «ENDFOR»
539                 «removeProperty(allProps, "key")»
540                 if (base.getKey() == null) {
541                     this._key = new «keyType.importedName»(
542                         «FOR keyProp : keyProps SEPARATOR ", "»
543                             base.«keyProp.getterMethodName»()
544                         «ENDFOR»
545                     );
546                     «FOR field : keyProps»
547                         this.«field.fieldName» = base.«field.getterMethodName»();
548                     «ENDFOR»
549                 } else {
550                     this._key = base.getKey();
551                     «FOR field : keyProps»
552                            this.«field.fieldName» = _key.«field.getterMethodName»();
553                     «ENDFOR»
554                 }
555             «ENDIF»
556             «FOR field : allProps»
557                 this.«field.fieldName» = base.«field.getterMethodName»();
558             «ENDFOR»
559             «IF augmentField != null»
560                 «IF impl»
561                     switch (base.«augmentField.name».size()) {
562                     case 0:
563                         this.«augmentField.name» = «Collections.importedName».emptyMap();
564                         break;
565                         case 1:
566                             final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
567                             this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
568                         break;
569                     default :
570                         this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
571                     }
572                 «ELSE»
573                     if (base instanceof «type.name»«IMPL») {
574                         «type.name»«IMPL» impl = («type.name»«IMPL») base;
575                         this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
576                     } else if (base instanceof «AugmentationHolder.importedName») {
577                         @SuppressWarnings("unchecked")
578                         «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
579                         this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
580                     }
581                 «ENDIF»
582             «ENDIF»
583         }
584     '''
585
586     private def boolean implementsIfc(GeneratedType type, Type impl) {
587         for (Type ifc : type.implements) {
588             if (ifc.equals(impl)) {
589                 return true;
590             }
591         }
592         return false;
593     }
594
595     private def Type getKey(GeneratedType type) {
596         for (m : type.methodDefinitions) {
597             if ("getKey".equals(m.name)) {
598                 return m.returnType;
599             }
600         }
601         return null;
602     }
603
604     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
605         var GeneratedProperty toRemove = null
606         for (p : props) {
607             if (p.name.equals(name)) {
608                 toRemove = p;
609             }
610         }
611         if (toRemove != null) {
612             props.remove(toRemove);
613         }
614     }
615
616     /**
617      * Template method which generate getter methods for IMPL class.
618      *
619      * @return string with getter methods
620      */
621     def private generateGetters(boolean addOverride) '''
622         «IF !properties.empty»
623             «FOR field : properties SEPARATOR '\n'»
624                 «IF addOverride»@Override«ENDIF»
625                 «field.getterMethod»
626             «ENDFOR»
627         «ENDIF»
628         «IF augmentField != null»
629
630             @SuppressWarnings("unchecked")
631             «IF addOverride»@Override«ENDIF»
632             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
633                 if (augmentationType == null) {
634                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
635                 }
636                 return (E) «augmentField.name».get(augmentationType);
637             }
638         «ENDIF»
639     '''
640
641     /**
642      * Template method which generates the method <code>hashCode()</code>.
643      *
644      * @return string with the <code>hashCode()</code> method definition in JAVA format
645      */
646     def protected generateHashCode() '''
647         «IF !properties.empty || augmentField != null»
648             @Override
649             public int hashCode() {
650                 final int prime = 31;
651                 int result = 1;
652                 «FOR property : properties»
653                     «IF property.returnType.name.contains("[")»
654                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
655                     «ELSE»
656                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
657                     «ENDIF»
658                 «ENDFOR»
659                 «IF augmentField != null»
660                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
661                 «ENDIF»
662                 return result;
663             }
664         «ENDIF»
665     '''
666
667     /**
668      * Template method which generates the method <code>equals()</code>.
669      *
670      * @return string with the <code>equals()</code> method definition in JAVA format
671      */
672     def protected generateEquals() '''
673         «IF !properties.empty || augmentField != null»
674             @Override
675             public boolean equals(«Object.importedName» obj) {
676                 if (this == obj) {
677                     return true;
678                 }
679                 if (!(obj instanceof «DataObject.importedName»)) {
680                     return false;
681                 }
682                 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
683                     return false;
684                 }
685                 «type.importedName» other = («type.importedName»)obj;
686                 «FOR property : properties»
687                     «val fieldName = property.fieldName»
688                     if («fieldName» == null) {
689                         if (other.«property.getterMethodName»() != null) {
690                             return false;
691                         }
692                     «IF property.returnType.name.contains("[")»
693                     } else if(!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
694                     «ELSE»
695                     } else if(!«fieldName».equals(other.«property.getterMethodName»())) {
696                     «ENDIF»
697                         return false;
698                     }
699                 «ENDFOR»
700                 «IF augmentField != null»
701                     if (getClass() == obj.getClass()) {
702                         // Simple case: we are comparing against self
703                         «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
704                         «val fieldName = augmentField.name»
705                         if («fieldName» == null) {
706                             if (otherImpl.«fieldName» != null) {
707                                 return false;
708                             }
709                         } else if(!«fieldName».equals(otherImpl.«fieldName»)) {
710                             return false;
711                         }
712                     } else {
713                         // Hard case: compare our augments with presence there...
714                         for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
715                             if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
716                                 return false;
717                             }
718                         }
719                         // .. and give the other one the chance to do the same
720                         if (!obj.equals(this)) {
721                             return false;
722                         }
723                     }
724                 «ENDIF»
725                 return true;
726             }
727         «ENDIF»
728     '''
729
730     def override generateToString(Collection<GeneratedProperty> properties) '''
731         «IF !(properties === null)»
732             @Override
733             public «String.importedName» toString() {
734                 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
735                 boolean first = true;
736
737                 «FOR property : properties»
738                     if («property.fieldName» != null) {
739                         if (first) {
740                             first = false;
741                         } else {
742                             builder.append(", ");
743                         }
744                         builder.append("«property.fieldName»=");
745                         «IF property.returnType.name.contains("[")»
746                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
747                         «ELSE»
748                             builder.append(«property.fieldName»);
749                         «ENDIF»
750                      }
751                 «ENDFOR»
752                 «IF augmentField != null»
753                     if (first) {
754                         first = false;
755                     } else {
756                         builder.append(", ");
757                     }
758                     builder.append("«augmentField.name»=");
759                     builder.append(«augmentField.name».values());
760                 «ENDIF»
761                 return builder.append(']').toString();
762             }
763         «ENDIF»
764     '''
765
766     override protected getFullyQualifiedName() {
767         '''«type.fullyQualifiedName»Builder'''.toString
768     }
769
770     def implementedInterfaceGetter() '''
771     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
772         return «type.importedName».class;
773     }
774     '''
775
776     private def createDescription(GeneratedType type) {
777         return '''
778         Class that builds {@link «type.importedName»} instances.
779
780         @see «type.importedName»
781     '''
782     }
783
784     override def protected String formatDataForJavaDoc(GeneratedType type) {
785         val typeDescription = createDescription(type)
786
787         return '''
788             «IF !typeDescription.nullOrEmpty»
789             «typeDescription»
790             «ENDIF»
791         '''.toString
792     }
793 }
794