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