Added constructors to builder classes based on implemented interfaces from uses nodes.
[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             «generateBuilderConstructor(type)»\r
207 \r
208             «generateGetters(false)»\r
209 \r
210             «generateSetters»\r
211 \r
212             public «type.name» build() {\r
213                 return new «type.name»«IMPL»(this);\r
214             }\r
215 \r
216             private static final class «type.name»«IMPL» implements «type.name» {\r
217 \r
218                 «implementedInterfaceGetter»\r
219 \r
220                 «generateFields(true)»\r
221 \r
222                 «generateConstructor»\r
223 \r
224                 «generateGetters(true)»\r
225 \r
226                 «generateHashCode()»\r
227 \r
228                 «generateEquals()»\r
229             }\r
230 \r
231         }\r
232     '''\r
233 \r
234 \r
235     def private generateBuilderConstructor(Type implementedIfc) '''\r
236         public «type.name»«BUILDER»() {\r
237         }\r
238 \r
239         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»\r
240         «val ifc = implementedIfc as GeneratedType»\r
241         «FOR impl : ifc.implements»\r
242             «IF (impl instanceof GeneratedType) &&  !((impl as GeneratedType).methodDefinitions.empty)»\r
243                 public «type.name»«BUILDER»(«impl.fullyQualifiedName» arg) {\r
244                     «printBuilderConstructorProperties(impl)»\r
245                 }\r
246             «ENDIF»\r
247         «ENDFOR»\r
248         «ENDIF»\r
249     '''\r
250 \r
251     def private printBuilderConstructorProperties(Type implementedIfc) '''\r
252         «IF (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))»\r
253         «val ifc = implementedIfc as GeneratedType»\r
254         «FOR getter : ifc.methodDefinitions»\r
255             this._«getter.propertyNameFromGetter» = arg.«getter.name»();\r
256         «ENDFOR»\r
257         «FOR impl : ifc.implements»\r
258         «printBuilderConstructorProperties(impl)»\r
259         «ENDFOR»\r
260         «ENDIF»\r
261     '''\r
262 \r
263         /**\r
264          * Template method which generates class attributes.\r
265          * \r
266          * @param boolean value which specify whether field is|isn't final\r
267          * @return string with class attributes and their types\r
268          */\r
269     def private generateFields(boolean _final) '''\r
270         «IF !properties.empty»\r
271             «FOR f : properties»\r
272                 private  «IF _final»final«ENDIF»  «f.returnType.importedName» «f.fieldName»;\r
273             «ENDFOR»\r
274         «ENDIF»\r
275         «IF augmentField != null»\r
276             private «Map.importedName»<Class<? extends «augmentField.returnType.importedName»>, «augmentField.returnType.importedName»> «augmentField.name» = new «HashMap.importedName»<>();\r
277         «ENDIF»\r
278     '''\r
279 \r
280         /**\r
281          * Template method which generates setter methods\r
282          * \r
283          * @return string with the setter methods \r
284          */\r
285     def private generateSetters() '''\r
286         «FOR field : properties SEPARATOR '\n'»\r
287             public «type.name»«BUILDER» set«field.name.toFirstUpper»(«field.returnType.importedName» value) {\r
288                 this.«field.fieldName» = value;\r
289                 return this;\r
290             }\r
291         «ENDFOR»\r
292         «IF augmentField != null»\r
293             \r
294             public «type.name»«BUILDER» add«augmentField.name.toFirstUpper»(Class<? extends «augmentField.returnType.importedName»> augmentationType, «augmentField.returnType.importedName» augmentation) {\r
295                 this.«augmentField.name».put(augmentationType, augmentation);\r
296                 return this;\r
297             }\r
298         «ENDIF»\r
299     '''\r
300     \r
301     /**\r
302      * Template method which generate constructor for IMPL class.\r
303      * \r
304      * @return string with IMPL class constructor\r
305      */\r
306     def private generateConstructor() '''\r
307         private «type.name»«IMPL»(«type.name»«BUILDER» builder) {\r
308             «IF !properties.empty»\r
309                 «FOR field : properties»\r
310                     this.«field.fieldName» = builder.«field.getterMethodName»();\r
311                 «ENDFOR»\r
312             «ENDIF»\r
313             «IF augmentField != null»\r
314                 this.«augmentField.name».putAll(builder.«augmentField.name»);\r
315             «ENDIF»\r
316         }\r
317     '''\r
318     \r
319 \r
320     /**\r
321      * Template method which generate getter methods for IMPL class.\r
322      * \r
323      * @return string with getter methods\r
324      */\r
325     def private generateGetters(boolean addOverride) '''\r
326         «IF !properties.empty»\r
327             «FOR field : properties SEPARATOR '\n'»\r
328                 «IF addOverride»@Override«ENDIF»\r
329                 «field.getterMethod»\r
330             «ENDFOR»\r
331         «ENDIF»\r
332         «IF augmentField != null»\r
333 \r
334             @SuppressWarnings("unchecked")\r
335             «IF addOverride»@Override«ENDIF»\r
336             public <E extends «augmentField.returnType.importedName»> E get«augmentField.name.toFirstUpper»(Class<E> augmentationType) {\r
337                 if (augmentationType == null) {\r
338                     throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");\r
339                 }\r
340                 return (E) «augmentField.name».get(augmentationType);\r
341             }\r
342         «ENDIF»\r
343     '''\r
344 \r
345     /**\r
346      * Template method which generates the method <code>hashCode()</code>.\r
347      * \r
348      * @return string with the <code>hashCode()</code> method definition in JAVA format\r
349      */\r
350     def protected generateHashCode() '''\r
351         «IF !properties.empty || augmentField != null»\r
352             @Override\r
353             public int hashCode() {\r
354                 final int prime = 31;\r
355                 int result = 1;\r
356                 «FOR property : properties»\r
357                     «IF property.returnType.name.contains("[")»\r
358                     result = prime * result + ((«property.fieldName» == null) ? 0 : java.util.Arrays.hashCode(«property.fieldName»));\r
359                     «ELSE»\r
360                     result = prime * result + ((«property.fieldName» == null) ? 0 : «property.fieldName».hashCode());\r
361                     «ENDIF»\r
362                 «ENDFOR»\r
363                 «IF augmentField != null»\r
364                     result = prime * result + ((«augmentField.name» == null) ? 0 : «augmentField.name».hashCode());\r
365                 «ENDIF»\r
366                 return result;\r
367             }\r
368         «ENDIF»\r
369     '''\r
370 \r
371     /**\r
372      * Template method which generates the method <code>equals()</code>.\r
373      * \r
374      * @return string with the <code>equals()</code> method definition in JAVA format     \r
375      */\r
376     def protected generateEquals() '''\r
377         «IF !properties.empty || augmentField != null»\r
378             @Override\r
379             public boolean equals(java.lang.Object obj) {\r
380                 if (this == obj) {\r
381                     return true;\r
382                 }\r
383                 if (obj == null) {\r
384                     return false;\r
385                 }\r
386                 if (getClass() != obj.getClass()) {\r
387                     return false;\r
388                 }\r
389                 «type.name»«IMPL» other = («type.name»«IMPL») obj;\r
390                 «FOR property : properties»\r
391                     «val fieldName = property.fieldName»\r
392                     if («fieldName» == null) {\r
393                         if (other.«fieldName» != null) {\r
394                             return false;\r
395                         }\r
396                     «IF property.returnType.name.contains("[")»\r
397                     } else if(!java.util.Arrays.equals(«fieldName», other.«fieldName»)) {\r
398                     «ELSE»\r
399                     } else if(!«fieldName».equals(other.«fieldName»)) {\r
400                     «ENDIF»\r
401                         return false;\r
402                     }\r
403                 «ENDFOR»\r
404                 «IF augmentField != null»\r
405                     «val fieldName = augmentField.name»\r
406                     if («fieldName» == null) {\r
407                         if (other.«fieldName» != null) {\r
408                             return false;\r
409                         }\r
410                     } else if(!«fieldName».equals(other.«fieldName»)) {\r
411                         return false;\r
412                     }\r
413                 «ENDIF»\r
414                 return true;\r
415             }\r
416         «ENDIF»\r
417     '''\r
418 \r
419     override protected getFullyQualifiedName() {\r
420         '''«type.fullyQualifiedName»Builder'''.toString\r
421     }\r
422     \r
423     def implementedInterfaceGetter() '''\r
424     public «Class.importedName»<«type.importedName»> getImplementedInterface() {\r
425         return «type.importedName».class;\r
426     }\r
427     '''\r
428     \r
429 }\r
430 \r