BUG-1485: remove deprecated range methods
[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 java.util.ArrayList
12 import java.util.Arrays
13 import java.util.Collection
14 import java.util.Collections
15 import java.util.HashMap
16 import java.util.HashSet
17 import java.util.LinkedHashSet
18 import java.util.List
19 import java.util.Map
20 import java.util.Set
21 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl
22 import org.opendaylight.yangtools.binding.generator.util.Types
23 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl
24 import org.opendaylight.yangtools.concepts.Builder
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.AugmentationHolder
33 import org.opendaylight.yangtools.yang.binding.DataObject
34 import org.opendaylight.yangtools.yang.binding.Identifiable
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             «IF !(impl.methodDefinitions.empty)»
281                 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {
282                     «printConstructorPropertySetter(impl)»
283                 }
284             «ENDIF»
285             «FOR implTypeImplement : impl.implements»
286                 «generateConstructorFromIfc(implTypeImplement)»
287             «ENDFOR»
288         «ENDIF»
289     '''
290
291     def private Object printConstructorPropertySetter(Type implementedIfc) '''
292         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
293             «val ifc = implementedIfc as GeneratedType»
294             «FOR getter : ifc.methodDefinitions»
295                 this._«getter.propertyNameFromGetter» = arg.«getter.name»();
296             «ENDFOR»
297             «FOR impl : ifc.implements»
298                 «printConstructorPropertySetter(impl)»
299             «ENDFOR»
300         «ENDIF»
301     '''
302
303     /**
304      * Generate 'fieldsFrom' method to set builder properties based on type of given argument.
305      */
306     def private generateMethodFieldsFrom(Type type) '''
307         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»
308             «val ifc = type as GeneratedType»
309             «IF ifc.hasImplementsFromUses»
310                 «val List<Type> done = ifc.getBaseIfcs»
311                 «generateMethodFieldsFromComment(ifc)»
312                 public void fieldsFrom(«DataObject.importedName» arg) {
313                     boolean isValidArg = false;
314                     «FOR impl : ifc.getAllIfcs»
315                         «generateIfCheck(impl, done)»
316                     «ENDFOR»
317                     if (!isValidArg) {
318                         throw new IllegalArgumentException(
319                           "expected one of: «ifc.getAllIfcs.toListOfNames» \n" +
320                           "but was: " + arg
321                         );
322                     }
323                 }
324             «ENDIF»
325         «ENDIF»
326     '''
327
328     def private generateMethodFieldsFromComment(GeneratedType type) '''
329         /**
330          *Set fields from given grouping argument. Valid argument is instance of one of following types:
331          * <ul>
332          «FOR impl : type.getAllIfcs»
333          * <li>«impl.fullyQualifiedName»</li>
334          «ENDFOR»
335          * </ul>
336          *
337          * @param arg grouping object
338          * @throws IllegalArgumentException if given argument is none of valid types
339         */
340     '''
341
342     /**
343      * Method is used to find out if given type implements any interface from uses.
344      */
345     def boolean hasImplementsFromUses(GeneratedType type) {
346         var i = 0
347         for (impl : type.getAllIfcs) {
348             if ((impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)) {
349                 i = i + 1
350             }
351         }
352         return i > 0
353     }
354
355     def private generateIfCheck(Type impl, List<Type> done) '''
356         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»
357             «val implType = impl as GeneratedType»
358             if (arg instanceof «implType.fullyQualifiedName») {
359                 «printPropertySetter(implType)»
360                 isValidArg = true;
361             }
362         «ENDIF»
363     '''
364
365     def private printPropertySetter(Type implementedIfc) '''
366         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»
367         «val ifc = implementedIfc as GeneratedType»
368         «FOR getter : ifc.methodDefinitions»
369             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();
370         «ENDFOR»
371         «ENDIF»
372     '''
373
374     private def List<Type> getBaseIfcs(GeneratedType type) {
375         val List<Type> baseIfcs = new ArrayList();
376         for (ifc : type.implements) {
377             if (ifc instanceof GeneratedType && !(ifc as GeneratedType).methodDefinitions.empty) {
378                 baseIfcs.add(ifc)
379             }
380         }
381         return baseIfcs
382     }
383
384     private def Set<Type> getAllIfcs(Type type) {
385         val Set<Type> baseIfcs = new HashSet()
386         if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
387             val ifc = type as GeneratedType
388             for (impl : ifc.implements) {
389                 if (impl instanceof GeneratedType && !(impl as GeneratedType).methodDefinitions.empty) {
390                     baseIfcs.add(impl)
391                 }
392                 baseIfcs.addAll(impl.getAllIfcs)
393             }
394         }
395         return baseIfcs
396     }
397
398     private def List<String> toListOfNames(Collection<Type> types) {
399         val List<String> names = new ArrayList
400         for (type : types) {
401             names.add(type.fullyQualifiedName)
402         }
403         return names
404     }
405
406     /**
407      * Template method which generates class attributes.
408      *
409      * @param boolean value which specify whether field is|isn't final
410      * @return string with class attributes and their types
411      */
412     def private generateFields(boolean _final) '''
413         «IF properties !== null»
414             «FOR f : properties»
415                 private«IF _final» final«ENDIF» «f.returnType.importedName» «f.fieldName»;
416                 «val restrictions = f.returnType.restrictions»
417                 «IF !_final && restrictions != null && !(restrictions.lengthConstraints.empty)»
418                     «LengthGenerator.generateLengthChecker(f.fieldName.toString, f.returnType, restrictions.lengthConstraints)»
419                 «ENDIF»
420             «ENDFOR»
421         «ENDIF»
422     '''
423
424     def private generateAugmentField(boolean isPrivate) '''
425         «IF augmentField != null»
426             «IF isPrivate»private «ENDIF»«Map.importedName»<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = «Collections.importedName».emptyMap();
427         «ENDIF»
428     '''
429
430     /**
431      * Template method which generates setter methods
432      *
433      * @return string with the setter methods
434      */
435     def private generateSetters() '''
436         «FOR field : properties SEPARATOR '\n'»
437             «val restrictions = field.returnType.restrictions»
438             «IF restrictions != null»
439                 «IF !restrictions.rangeConstraints.nullOrEmpty»
440                     «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
441                     «rangeGenerator.generateRangeChecker(field.name.toFirstUpper, restrictions.rangeConstraints)»
442
443                 «ENDIF»
444             «ENDIF»
445             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {
446                 «IF restrictions != null»
447                 if (value != null) {
448                     «IF !restrictions.rangeConstraints.nullOrEmpty»
449                         «val rangeGenerator = AbstractRangeGenerator.forType(field.returnType)»
450                         «IF field.returnType instanceof ConcreteType»
451                             «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value")»
452                         «ELSE»
453                             «rangeGenerator.generateRangeCheckerCall(field.name.toFirstUpper, "value.getValue()")»
454                         «ENDIF»
455                     «ENDIF»
456                     «generateRestrictions(field, "value")»
457                 }
458                 «ENDIF»
459                 this.«field.fieldName» = value;
460                 return this;
461             }
462         «ENDFOR»
463         «IF augmentField != null»
464
465             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {
466                 if (augmentation == null) {
467                     return remove«augmentField.name.toFirstUpper»(augmentationType);
468                 }
469
470                 if (!(this.«augmentField.name» instanceof «HashMap.importedName»)) {
471                     this.«augmentField.name» = new «HashMap.importedName»<>();
472                 }
473
474                 this.«augmentField.name».put(augmentationType, augmentation);
475                 return this;
476             }
477
478             public «type.name»«BUILDER» remove«augmentField.name.toFirstUpper»(«Class.importedName»<? extends «augmentField.returnType.importedName»> augmentationType) {
479                 if (this.«augmentField.name» instanceof «HashMap.importedName») {
480                     this.«augmentField.name».remove(augmentationType);
481                 }
482                 return this;
483             }
484         «ENDIF»
485     '''
486
487     def private generateRestrictions(GeneratedProperty field, String paramName) '''
488         «val Type type = field.returnType»
489         «val restrictions = type.getRestrictions»
490         «IF restrictions !== null && !restrictions.lengthConstraints.empty»
491             «IF type instanceof ConcreteType»
492                 «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, paramName)»
493             «ELSE»
494                 «LengthGenerator.generateLengthCheckerCall(field.fieldName.toString, paramName + ".getValue()")»
495             «ENDIF»
496         «ENDIF»
497     '''
498
499     def private CharSequence generateCopyConstructor(boolean impl) '''
500         «IF impl»private«ELSE»public«ENDIF» «type.name»«IF impl»«IMPL»«ELSE»«BUILDER»«ENDIF»(«type.name»«IF impl»«BUILDER»«ENDIF» base) {
501             «val allProps = new ArrayList(properties)»
502             «val isList = implementsIfc(type, Types.parameterizedTypeFor(Types.typeForClass(Identifiable), type))»
503             «val keyType = type.getKey»
504             «IF isList && keyType != null»
505                 «val keyProps = new ArrayList((keyType as GeneratedTransferObject).properties)»
506                 «Collections.sort(keyProps,
507                     [ p1, p2 |
508                         return p1.name.compareTo(p2.name)
509                     ])
510                 »
511                 «FOR field : keyProps»
512                     «removeProperty(allProps, field.name)»
513                 «ENDFOR»
514                 «removeProperty(allProps, "key")»
515                 if (base.getKey() == null) {
516                     this._key = new «keyType.importedName»(
517                         «FOR keyProp : keyProps SEPARATOR ", "»
518                             base.«keyProp.getterMethodName»()
519                         «ENDFOR»
520                     );
521                     «FOR field : keyProps»
522                         this.«field.fieldName» = base.«field.getterMethodName»();
523                     «ENDFOR»
524                 } else {
525                     this._key = base.getKey();
526                     «FOR field : keyProps»
527                            this.«field.fieldName» = _key.«field.getterMethodName»();
528                     «ENDFOR»
529                 }
530             «ENDIF»
531             «FOR field : allProps»
532                 this.«field.fieldName» = base.«field.getterMethodName»();
533             «ENDFOR»
534             «IF augmentField != null»
535                 «IF impl»
536                     switch (base.«augmentField.name».size()) {
537                     case 0:
538                         this.«augmentField.name» = «Collections.importedName».emptyMap();
539                         break;
540                     case 1:
541                         final «Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e = base.«augmentField.name».entrySet().iterator().next();
542                         this.«augmentField.name» = «Collections.importedName».<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»>singletonMap(e.getKey(), e.getValue());
543                         break;
544                     default :
545                         this.«augmentField.name» = new «HashMap.importedName»<>(base.«augmentField.name»);
546                     }
547                 «ELSE»
548                     if (base instanceof «type.name»«IMPL») {
549                         «type.name»«IMPL» impl = («type.name»«IMPL») base;
550                         if (!impl.«augmentField.name».isEmpty()) {
551                             this.«augmentField.name» = new «HashMap.importedName»<>(impl.«augmentField.name»);
552                         }
553                     } else if (base instanceof «AugmentationHolder.importedName») {
554                         @SuppressWarnings("unchecked")
555                         «AugmentationHolder.importedName»<«type.importedName»> casted =(«AugmentationHolder.importedName»<«type.importedName»>) base;
556                         if (!casted.augmentations().isEmpty()) {
557                             this.«augmentField.name» = new «HashMap.importedName»<>(casted.augmentations());
558                         }
559                     }
560                 «ENDIF»
561             «ENDIF»
562         }
563     '''
564
565     private def boolean implementsIfc(GeneratedType type, Type impl) {
566         for (Type ifc : type.implements) {
567             if (ifc.equals(impl)) {
568                 return true;
569             }
570         }
571         return false;
572     }
573
574     private def Type getKey(GeneratedType type) {
575         for (m : type.methodDefinitions) {
576             if ("getKey".equals(m.name)) {
577                 return m.returnType;
578             }
579         }
580         return null;
581     }
582
583     private def void removeProperty(Collection<GeneratedProperty> props, String name) {
584         var GeneratedProperty toRemove = null
585         for (p : props) {
586             if (p.name.equals(name)) {
587                 toRemove = p;
588             }
589         }
590         if (toRemove != null) {
591             props.remove(toRemove);
592         }
593     }
594
595     /**
596      * Template method which generate getter methods for IMPL class.
597      *
598      * @return string with getter methods
599      */
600     def private generateGetters(boolean addOverride) '''
601         «IF !properties.empty»
602             «FOR field : properties SEPARATOR '\n'»
603                 «IF addOverride»@Override«ENDIF»
604                 «field.getterMethod»
605             «ENDFOR»
606         «ENDIF»
607         «IF augmentField != null»
608
609             @SuppressWarnings("unchecked")
610             «IF addOverride»@Override«ENDIF»
611             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(«Class.importedName»<E> augmentationType) {
612                 if (augmentationType == null) {
613                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
614                 }
615                 return (E) «augmentField.name».get(augmentationType);
616             }
617         «ENDIF»
618     '''
619
620     /**
621      * Template method which generates the method <code>hashCode()</code>.
622      *
623      * @return string with the <code>hashCode()</code> method definition in JAVA format
624      */
625     def protected generateHashCode() '''
626         «IF !properties.empty || augmentField != null»
627             private int hash = 0;
628             private volatile boolean hashValid = false;
629
630             @Override
631             public int hashCode() {
632                 if (hashValid) {
633                     return hash;
634                 }
635
636                 final int prime = 31;
637                 int result = 1;
638                 «FOR property : properties»
639                     «IF property.returnType.name.contains("[")»
640                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));
641                     «ELSE»
642                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());
643                     «ENDIF»
644                 «ENDFOR»
645                 «IF augmentField != null»
646                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());
647                 «ENDIF»
648
649                 hash = result;
650                 hashValid = true;
651                 return result;
652             }
653         «ENDIF»
654     '''
655
656     /**
657      * Template method which generates the method <code>equals()</code>.
658      *
659      * @return string with the <code>equals()</code> method definition in JAVA format
660      */
661     def protected generateEquals() '''
662         «IF !properties.empty || augmentField != null»
663             @Override
664             public boolean equals(«Object.importedName» obj) {
665                 if (this == obj) {
666                     return true;
667                 }
668                 if (!(obj instanceof «DataObject.importedName»)) {
669                     return false;
670                 }
671                 if (!«type.importedName».class.equals(((«DataObject.importedName»)obj).getImplementedInterface())) {
672                     return false;
673                 }
674                 «type.importedName» other = («type.importedName»)obj;
675                 «FOR property : properties»
676                     «val fieldName = property.fieldName»
677                     if («fieldName» == null) {
678                         if (other.«property.getterMethodName»() != null) {
679                             return false;
680                         }
681                     «IF property.returnType.name.contains("[")»
682                     } else if(!«Arrays.importedName».equals(«fieldName», other.«property.getterMethodName»())) {
683                     «ELSE»
684                     } else if(!«fieldName».equals(other.«property.getterMethodName»())) {
685                     «ENDIF»
686                         return false;
687                     }
688                 «ENDFOR»
689                 «IF augmentField != null»
690                     if (getClass() == obj.getClass()) {
691                         // Simple case: we are comparing against self
692                         «type.name»«IMPL» otherImpl = («type.name»«IMPL») obj;
693                         «val fieldName = augmentField.name»
694                         if («fieldName» == null) {
695                             if (otherImpl.«fieldName» != null) {
696                                 return false;
697                             }
698                         } else if(!«fieldName».equals(otherImpl.«fieldName»)) {
699                             return false;
700                         }
701                     } else {
702                         // Hard case: compare our augments with presence there...
703                         for («Map.importedName».Entry<«Class.importedName»<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> e : «augmentField.name».entrySet()) {
704                             if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
705                                 return false;
706                             }
707                         }
708                         // .. and give the other one the chance to do the same
709                         if (!obj.equals(this)) {
710                             return false;
711                         }
712                     }
713                 «ENDIF»
714                 return true;
715             }
716         «ENDIF»
717     '''
718
719     def override generateToString(Collection<GeneratedProperty> properties) '''
720         «IF !(properties === null)»
721             @Override
722             public «String.importedName» toString() {
723                 «StringBuilder.importedName» builder = new «StringBuilder.importedName» ("«type.name» [");
724                 boolean first = true;
725
726                 «FOR property : properties»
727                     if («property.fieldName» != null) {
728                         if (first) {
729                             first = false;
730                         } else {
731                             builder.append(", ");
732                         }
733                         builder.append("«property.fieldName»=");
734                         «IF property.returnType.name.contains("[")»
735                             builder.append(«Arrays.importedName».toString(«property.fieldName»));
736                         «ELSE»
737                             builder.append(«property.fieldName»);
738                         «ENDIF»
739                      }
740                 «ENDFOR»
741                 «IF augmentField != null»
742                     if (first) {
743                         first = false;
744                     } else {
745                         builder.append(", ");
746                     }
747                     builder.append("«augmentField.name»=");
748                     builder.append(«augmentField.name».values());
749                 «ENDIF»
750                 return builder.append(']').toString();
751             }
752         «ENDIF»
753     '''
754
755     def implementedInterfaceGetter() '''
756     public «Class.importedName»<«type.importedName»> getImplementedInterface() {
757         return «type.importedName».class;
758     }
759     '''
760
761     private def createDescription(GeneratedType type) {
762         return '''
763         Class that builds {@link «type.importedName»} instances.
764
765         @see «type.importedName»
766     '''
767     }
768
769     override def protected String formatDataForJavaDoc(GeneratedType type) {
770         val typeDescription = createDescription(type)
771
772         return '''
773             «IF !typeDescription.nullOrEmpty»
774             «typeDescription»
775             «ENDIF»
776         '''.toString
777     }
778 }
779