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