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