Implemented length statement. Added new method to Builder classes.
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / BuilderTemplate.xtend
1 package org.opendaylight.yangtools.sal.java.api.generator\r
2 \r
3 import java.util.Arrays;\r
4 import java.util.LinkedHashSet\r
5 import java.util.List\r
6 import java.util.Map\r
7 import java.util.Set\r
8 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl\r
9 import org.opendaylight.yangtools.binding.generator.util.Types\r
10 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTOBuilderImpl\r
11 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty\r
12 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject\r
13 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType\r
14 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature\r
15 import org.opendaylight.yangtools.sal.binding.model.api.Type\r
16 import org.opendaylight.yangtools.yang.binding.Augmentable\r
17 import static org.opendaylight.yangtools.binding.generator.util.Types.*\r
18 import java.util.HashMap\r
19 import java.util.Collections\rimport org.opendaylight.yangtools.yang.binding.DataObject
20 import java.util.ArrayList
21
22 /**\r
23  * Template for generating JAVA builder classes. \r
24  */\r
25 \r
26 class BuilderTemplate extends BaseTemplate {\r
27     \r
28     /**\r
29      * Constant with the name of the concrete method.\r
30      */\r
31     val static GET_AUGMENTATION_METHOD_NAME = "getAugmentation"\r
32     \r
33     /**\r
34      * Constant with the suffix for builder classes.\r
35      */\r
36     val static BUILDER = 'Builder'\r
37     \r
38     /**\r
39      * Constant with suffix for the classes which are generated from the builder classes.\r
40      */\r
41     val static IMPL = 'Impl'\r
42     \r
43     /**\r
44      * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME\r
45      */\r
46     var GeneratedProperty augmentField\r
47     \r
48     /**\r
49      * Set of class attributes (fields) which are derived from the getter methods names\r
50      */\r
51     val Set<GeneratedProperty> properties\r
52     \r
53     /**\r
54      * Constructs new instance of this class.\r
55      * @throws IllegalArgumentException if <code>genType</code> equals <code>null</code>\r
56      */\r
57     new(GeneratedType genType) {\r
58         super(genType)\r
59         this.properties = propertiesFromMethods(createMethods)\r
60     }\r
61     \r
62     /**\r
63      * Returns set of method signature instances which contains all the methods of the <code>genType</code>\r
64      * and all the methods of the implemented interfaces.\r
65      * \r
66      * @returns set of method signature instances\r
67      */\r
68     def private Set<MethodSignature> createMethods() {\r
69         val Set<MethodSignature> methods = new LinkedHashSet\r
70         methods.addAll(type.methodDefinitions)\r
71         collectImplementedMethods(methods, type.implements)\r
72         return methods\r
73     }\r
74     \r
75 \r
76     /**\r
77      * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code> \r
78      * and recursivelly their implemented interfaces.\r
79      * \r
80      * @param methods set of method signatures\r
81      * @param implementedIfcs list of implemented interfaces\r
82      */\r
83     def private void collectImplementedMethods(Set<MethodSignature> methods, List<Type> implementedIfcs) {\r
84         if (implementedIfcs == null || implementedIfcs.empty) {\r
85             return\r
86         }\r
87         for (implementedIfc : implementedIfcs) {\r
88             if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {\r
89                 val ifc = implementedIfc as GeneratedType\r
90                 methods.addAll(ifc.methodDefinitions)\r
91                 collectImplementedMethods(methods, ifc.implements)\r
92             } else if (implementedIfc.fullyQualifiedName == Augmentable.name) {\r
93                 for (m : Augmentable.methods) {\r
94                     if (m.name == GET_AUGMENTATION_METHOD_NAME) {\r
95                         //addToImports(JAVA_UTIL, HASH_MAP)\r
96                         //addToImports(JAVA_UTIL, MAP)\r
97                         val fullyQualifiedName = m.returnType.name\r
98                         val pkg = fullyQualifiedName.package\r
99                         val name = fullyQualifiedName.name\r
100                         //addToImports(pkg, name)\r
101                         val tmpGenTO = new GeneratedTOBuilderImpl(pkg, name)\r
102                         val refType = new ReferencedTypeImpl(pkg, name)\r
103                         val generic = new ReferencedTypeImpl(type.packageName, type.name)\r
104                         val parametrizedReturnType = Types.parameterizedTypeFor(refType, generic)\r
105                         tmpGenTO.addMethod(m.name).setReturnType(parametrizedReturnType)\r
106                         augmentField = tmpGenTO.toInstance.methodDefinitions.first.propertyFromGetter\r
107                     }\r
108                 }\r
109             }\r
110         }\r
111     }\r
112     \r
113     \r
114     /**\r
115      * Returns the first element of the list <code>elements</code>.\r
116      * \r
117      * @param list of elements\r
118      */\r
119     def private <E> first(List<E> elements) {\r
120         elements.get(0)\r
121     }\r
122     \r
123     /**\r
124      * Returns the name of the package from <code>fullyQualifiedName</code>.\r
125      * \r
126      * @param fullyQualifiedName string with fully qualified type name (package + type)\r
127      * @return string with the package name\r
128      */\r
129     def private String getPackage(String fullyQualifiedName) {\r
130         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)\r
131         return if (lastDotIndex == -1) "" else fullyQualifiedName.substring(0, lastDotIndex)\r
132     }\r
133 \r
134         /**\r
135          * Returns the name of tye type from <code>fullyQualifiedName</code>\r
136          * \r
137          * @param fullyQualifiedName string with fully qualified type name (package + type)\r
138          * @return string with the name of the type\r
139          */\r
140     def private String getName(String fullyQualifiedName) {\r
141         val lastDotIndex = fullyQualifiedName.lastIndexOf(Constants.DOT)\r
142         return if (lastDotIndex == -1) fullyQualifiedName else fullyQualifiedName.substring(lastDotIndex + 1)\r
143     }\r
144     \r
145 \r
146     /**\r
147      * Creates set of generated property instances from getter <code>methods</code>.\r
148      * \r
149      * @param set of method signature instances which should be transformed to list of properties \r
150      * @return set of generated property instances which represents the getter <code>methods</code>\r
151      */\r
152     def private propertiesFromMethods(Set<MethodSignature> methods) {\r
153         \r
154 \r
155         if (methods == null || methods.isEmpty()) {\r
156             return Collections.emptySet\r
157         }\r
158         val Set<GeneratedProperty> result = new LinkedHashSet\r
159         for (m : methods) {\r
160             val createdField = m.propertyFromGetter\r
161             if (createdField != null) {\r
162                 result.add(createdField)\r
163             }\r
164         }\r
165         return result\r
166     }\r
167     \r
168     /**\r
169      * Creates generated property instance from the getter <code>method</code> name and return type.\r
170      * \r
171      * @param method method signature from which is the method name and return type obtained\r
172      * @return generated property instance for the getter <code>method</code>\r
173      * @throws IllegalArgumentException<ul>\r
174      *  <li>if the <code>method</code> equals <code>null</code></li>\r
175      *  <li>if the name of the <code>method</code> equals <code>null</code></li>\r
176      *  <li>if the name of the <code>method</code> is empty</li>\r
177      *  <li>if the return type of the <code>method</code> equals <code>null</code></li>\r
178      * </ul>\r
179      */\r
180     def private GeneratedProperty propertyFromGetter(MethodSignature method) {\r
181 \r
182         if (method == null || method.name == null || method.name.empty || method.returnType == null) {\r
183             throw new IllegalArgumentException("Method, method name, method return type reference cannot be NULL or empty!")\r
184         }\r
185         var prefix = "get";\r
186         if(BOOLEAN.equals(method.returnType)) {\r
187             prefix = "is";\r
188         } \r
189         if (method.name.startsWith(prefix)) {\r
190             val fieldName = method.getName().substring(prefix.length()).toFirstLower\r
191             val tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo")\r
192             tmpGenTO.addProperty(fieldName).setReturnType(method.returnType)\r
193             return tmpGenTO.toInstance.properties.first\r
194         }\r
195     }\r
196 \r
197     /**\r
198      * Template method which generates JAVA class body for builder class and for IMPL class. \r
199      * \r
200      * @return string with JAVA source code\r
201      */\r
202     override body() '''\r
203 \r
204         public class «type.name»«BUILDER» {\r
205 \r
206             «generateFields(false)»\r
207 \r
208             «generateConstructorsFromIfcs(type)»\r
209 \r
210             «generateSetterFromIfcs(type)»\r
211 \r
212             «generateGetters(false)»\r
213 \r
214             «generateSetters»\r
215 \r
216             public «type.name» build() {\r
217                 return new «type.name»«IMPL»(this);\r
218             }\r
219 \r
220             private static final class «type.name»«IMPL» implements «type.name» {\r
221 \r
222                 «implementedInterfaceGetter»\r
223 \r
224                 «generateFields(true)»\r
225 \r
226                 «generateConstructor»\r
227 \r
228                 «generateGetters(true)»\r
229 \r
230                 «generateHashCode()»\r
231 \r
232                 «generateEquals()»\r
233             }\r
234 \r
235         }\r
236     '''\r
237 \r
238     def private generateConstructorsFromIfcs(Type type) '''\r
239         public «type.name»«BUILDER»() {\r
240         } \r
241         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»\r
242         «val ifc = type as GeneratedType»\r
243         «FOR impl : ifc.implements»\r
244             «generateConstructorFromIfc(impl)»\r
245         «ENDFOR»\r
246         «ENDIF»\r
247     '''\r
248 \r
249     def private generateConstructorFromIfc(Type impl) '''\r
250         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»\r
251         «val implType = impl as GeneratedType»\r
252 \r
253         public «type.name»«BUILDER»(«implType.fullyQualifiedName» arg) {\r
254             «printConstructorProperties(implType)»\r
255         }\r
256         «FOR implTypeImplement : implType.implements»\r
257             «generateConstructorFromIfc(implTypeImplement)»\r
258         «ENDFOR»\r
259         «ENDIF»\r
260     '''\r
261 \r
262     def private printConstructorProperties(Type implementedIfc) '''\r
263         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»\r
264         «val ifc = implementedIfc as GeneratedType»\r
265         «FOR getter : ifc.methodDefinitions»\r
266             this._«getter.propertyNameFromGetter» = arg.«getter.name»();\r
267         «ENDFOR»\r
268         «FOR impl : ifc.implements»\r
269         «printConstructorProperties(impl)»\r
270         «ENDFOR»\r
271         «ENDIF»\r
272     '''\r
273 \r
274     def private generateSetterFromIfcs(Type type) '''\r
275         «IF (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject))»\r
276         «val ifc = type as GeneratedType»\r
277         «val List<Type> done = new ArrayList()»\r
278         public void fieldsFrom(«DataObject.importedName» arg) {\r
279             «FOR impl : ifc.implements»\r
280                 «generateSettersForIfc(impl, done)»\r
281             «ENDFOR»\r
282         }\r
283         «ENDIF»\r
284     '''\r
285 \r
286     def private generateSettersForIfc(Type impl, List<Type> done) '''\r
287         «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»\r
288             «val implType = impl as GeneratedType»\r
289             «val boolean added = done.contains(impl)»\r
290             «IF !(added)»\r
291                 if (arg instanceof «implType.fullyQualifiedName») {\r
292                     «printSetterProperties(implType, done)»\r
293                 }\r
294             «ENDIF»\r
295             «FOR implTypeImplement : implType.implements»\r
296                 «generateSettersForIfc(implTypeImplement, done)»\r
297             «ENDFOR»\r
298         «ENDIF»\r
299     '''\r
300 \r
301     def private printSetterProperties(Type implementedIfc, List<Type> done) '''\r
302         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»\r
303         «val ifc = implementedIfc as GeneratedType»\r
304         «val boolean added = done.contains(ifc)»\r
305         «IF !(added)»\r
306         «FOR getter : ifc.methodDefinitions»\r
307             this._«getter.propertyNameFromGetter» = ((«implementedIfc.fullyQualifiedName»)arg).«getter.name»();\r
308         «ENDFOR»\r
309         «val add = done.add(ifc)»\r
310         «ENDIF»\r
311         «FOR impl : ifc.implements»\r
312         «printSetterProperties(impl, done)»\r
313         «ENDFOR»\r
314         «ENDIF»\r
315     '''\r
316 \r
317         /**\r
318          * Template method which generates class attributes.\r
319          * \r
320          * @param boolean value which specify whether field is|isn't final\r
321          * @return string with class attributes and their types\r
322          */\r
323     def private generateFields(boolean _final) '''\r
324         «IF !properties.empty»\r
325             «FOR f : properties»\r
326                 private  «IF _final»final«ENDIF»  «f.returnType.importedName» «f.fieldName»;\r
327             «ENDFOR»\r
328         «ENDIF»\r
329         «IF augmentField != null»\r
330             private «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();\r
331         «ENDIF»\r
332     '''\r
333 \r
334         /**\r
335          * Template method which generates setter methods\r
336          * \r
337          * @return string with the setter methods \r
338          */\r
339     def private generateSetters() '''\r
340         «FOR field : properties SEPARATOR '\n'»\r
341             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {\r
342                 «generateLengthRestrictions(field.returnType, "value")»\r
343 \r
344                 this.«field.fieldName» = value;\r
345                 return this;\r
346             }\r
347         «ENDFOR»\r
348         «IF augmentField != null»\r
349             \r
350             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {\r
351                 this.«augmentField.name».put(augmentationType, augmentation);\r
352                 return this;\r
353             }\r
354         «ENDIF»\r
355     '''\r
356 \r
357     /**\r
358      * Template method which generate constructor for IMPL class.\r
359      * \r
360      * @return string with IMPL class constructor\r
361      */\r
362     def private generateConstructor() '''\r
363         private «type.name»«IMPL»(«type.name»«BUILDER» builder) {\r
364             «IF !properties.empty»\r
365                 «FOR field : properties»\r
366                     this.«field.fieldName» = builder.«field.getterMethodName»();\r
367                 «ENDFOR»\r
368             «ENDIF»\r
369             «IF augmentField != null»\r
370                 this.«augmentField.name».putAll(builder.«augmentField.name»);\r
371             «ENDIF»\r
372         }\r
373     '''\r
374     \r
375 \r
376     /**\r
377      * Template method which generate getter methods for IMPL class.\r
378      * \r
379      * @return string with getter methods\r
380      */\r
381     def private generateGetters(boolean addOverride) '''\r
382         «IF !properties.empty»\r
383             «FOR field : properties SEPARATOR '\n'»\r
384                 «IF addOverride»@Override«ENDIF»\r
385                 «field.getterMethod»\r
386             «ENDFOR»\r
387         «ENDIF»\r
388         «IF augmentField != null»\r
389 \r
390             @SuppressWarnings("unchecked")\r
391             «IF addOverride»@Override«ENDIF»\r
392             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {\r
393                 if (augmentationType == null) {\r
394                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");\r
395                 }\r
396                 return (E) «augmentField.name».get(augmentationType);\r
397             }\r
398         «ENDIF»\r
399     '''\r
400 \r
401     /**\r
402      * Template method which generates the method <code>hashCode()</code>.\r
403      * \r
404      * @return string with the <code>hashCode()</code> method definition in JAVA format\r
405      */\r
406     def protected generateHashCode() '''\r
407         «IF !properties.empty || augmentField != null»\r
408             @Override\r
409             public int hashCode() {\r
410                 final int prime = 31;\r
411                 int result = 1;\r
412                 «FOR property : properties»\r
413                     «IF property.returnType.name.contains("[")»\r
414                     result = prime * result + ((«property.fieldName» == null) ? 0 : «Arrays.importedName».hashCode(«property.fieldName»));\r
415                     «ELSE»\r
416                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());\r
417                     «ENDIF»\r
418                 «ENDFOR»\r
419                 «IF augmentField != null»\r
420                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());\r
421                 «ENDIF»\r
422                 return result;\r
423             }\r
424         «ENDIF»\r
425     '''\r
426 \r
427     /**\r
428      * Template method which generates the method <code>equals()</code>.\r
429      * \r
430      * @return string with the <code>equals()</code> method definition in JAVA format     \r
431      */\r
432     def protected generateEquals() '''\r
433         «IF !properties.empty || augmentField != null»\r
434             @Override\r
435             public boolean equals(java.lang.Object obj) {\r
436                 if (this == obj) {\r
437                     return true;\r
438                 }\r
439                 if (obj == null) {\r
440                     return false;\r
441                 }\r
442                 if (getClass() != obj.getClass()) {\r
443                     return false;\r
444                 }\r
445                 «type.name»«IMPL» other = («type.name»«IMPL») obj;\r
446                 «FOR property : properties»\r
447                     «val fieldName = property.fieldName»\r
448                     if («fieldName» == null) {\r
449                         if (other.«fieldName» != null) {\r
450                             return false;\r
451                         }\r
452                     «IF property.returnType.name.contains("[")»\r
453                     } else if(!«Arrays.importedName».equals(«fieldName», other.«fieldName»)) {\r
454                     «ELSE»\r
455                     } else if(!«fieldName».equals(other.«fieldName»)) {\r
456                     «ENDIF»\r
457                         return false;\r
458                     }\r
459                 «ENDFOR»\r
460                 «IF augmentField != null»\r
461                     «val fieldName = augmentField.name»\r
462                     if («fieldName» == null) {\r
463                         if (other.«fieldName» != null) {\r
464                             return false;\r
465                         }\r
466                     } else if(!«fieldName».equals(other.«fieldName»)) {\r
467                         return false;\r
468                     }\r
469                 «ENDIF»\r
470                 return true;\r
471             }\r
472         «ENDIF»\r
473     '''\r
474 \r
475     override protected getFullyQualifiedName() {\r
476         '''«type.fullyQualifiedName»Builder'''.toString\r
477     }\r
478     \r
479     def implementedInterfaceGetter() '''\r
480     public «Class.importedName»<«type.importedName»> getImplementedInterface() {\r
481         return «type.importedName».class;\r
482     }\r
483     '''\r
484     \r
485 }\r
486 \r