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