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