Updated code generation
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / java / api / generator / GeneratorUtil.java
1 /*\r
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.\r
3  *\r
4  * This program and the accompanying materials are made available under the\r
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
6  * and is available at http://www.eclipse.org/legal/epl-v10.html\r
7  */\r
8 package org.opendaylight.yangtools.sal.java.api.generator;\r
9 \r
10 import static org.opendaylight.yangtools.sal.java.api.generator.Constants.COMMA;\r
11 \r
12 import java.util.ArrayList;\r
13 import java.util.LinkedHashMap;\r
14 import java.util.List;\r
15 import java.util.Map;\r
16 \r
17 import org.opendaylight.yangtools.binding.generator.util.TypeConstants;\r
18 import org.opendaylight.yangtools.binding.generator.util.Types;\r
19 import org.opendaylight.yangtools.sal.binding.model.api.Constant;\r
20 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedProperty;\r
21 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedTransferObject;\r
22 import org.opendaylight.yangtools.sal.binding.model.api.GeneratedType;\r
23 import org.opendaylight.yangtools.sal.binding.model.api.MethodSignature;\r
24 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType;\r
25 import org.opendaylight.yangtools.sal.binding.model.api.Type;\r
26 import org.opendaylight.yangtools.sal.binding.model.api.WildcardType;\r
27 \r
28 public final class GeneratorUtil {\r
29 \r
30     /**\r
31      * It doesn't have the sense to create the instances of this class.\r
32      */\r
33     private GeneratorUtil() {\r
34     }\r
35 \r
36     /**\r
37      * Returns the map of imports. The map maps the type name to the package\r
38      * name. To the map are added packages for <code>genType</code> and for all\r
39      * enclosed types, constants, methods (parameter types, return values),\r
40      * implemented types.\r
41      * \r
42      * @param genType\r
43      *            generated type for which the map of the imports is created\r
44      * @return map of the necessary imports\r
45      * @throws IllegalArgumentException\r
46      *             if <code>genType</code> equals <code>null</code>\r
47      */\r
48     public static Map<String, String> createImports(GeneratedType genType) {\r
49         if (genType == null) {\r
50             throw new IllegalArgumentException("Generated Type cannot be NULL!");\r
51         }\r
52         final Map<String, String> imports = new LinkedHashMap<>();\r
53 \r
54         List<GeneratedType> childGeneratedTypes = genType.getEnclosedTypes();\r
55         if (!childGeneratedTypes.isEmpty()) {\r
56             for (GeneratedType genTypeChild : childGeneratedTypes) {\r
57                 imports.putAll(createImports(genTypeChild));\r
58             }\r
59         }\r
60 \r
61         final List<Constant> constants = genType.getConstantDefinitions();\r
62         final List<MethodSignature> methods = genType.getMethodDefinitions();\r
63         final List<Type> impl = genType.getImplements();\r
64 \r
65         // IMPLEMENTATIONS\r
66         if (impl != null) {\r
67             for (final Type type : impl) {\r
68                 putTypeIntoImports(genType, type, imports);\r
69             }\r
70         }\r
71 \r
72         // CONSTANTS\r
73         if (constants != null) {\r
74             for (final Constant constant : constants) {\r
75                 final Type constantType = constant.getType();\r
76                 putTypeIntoImports(genType, constantType, imports);\r
77             }\r
78         }\r
79 \r
80         // REGULAR EXPRESSION\r
81         if (genType instanceof GeneratedTransferObject) {\r
82             if (isConstantInTO(TypeConstants.PATTERN_CONSTANT_NAME, (GeneratedTransferObject) genType)) {\r
83                 putTypeIntoImports(genType, Types.typeForClass(java.util.regex.Pattern.class), imports);\r
84                 putTypeIntoImports(genType, Types.typeForClass(java.util.Arrays.class), imports);\r
85                 putTypeIntoImports(genType, Types.typeForClass(java.util.ArrayList.class), imports);\r
86             }\r
87         }\r
88 \r
89         // METHODS\r
90         if (methods != null) {\r
91             for (final MethodSignature method : methods) {\r
92                 final Type methodReturnType = method.getReturnType();\r
93                 putTypeIntoImports(genType, methodReturnType, imports);\r
94                 for (final MethodSignature.Parameter methodParam : method.getParameters()) {\r
95                     putTypeIntoImports(genType, methodParam.getType(), imports);\r
96                 }\r
97             }\r
98         }\r
99 \r
100         // PROPERTIES\r
101         if (genType instanceof GeneratedTransferObject) {\r
102             final GeneratedTransferObject genTO = (GeneratedTransferObject) genType;\r
103             final List<GeneratedProperty> properties = genTO.getProperties();\r
104             if (properties != null) {\r
105                 for (GeneratedProperty property : properties) {\r
106                     final Type propertyType = property.getReturnType();\r
107                     putTypeIntoImports(genType, propertyType, imports);\r
108                 }\r
109             }\r
110         }\r
111 \r
112         return imports;\r
113     }\r
114 \r
115     /**\r
116      * Evaluates if it is necessary to add the package name for\r
117      * <code>type</code> to the map of imports for <code>parentGenType</code>.\r
118      * If it is so the package name is saved to the map <code>imports</code>.\r
119      * \r
120      * @param parentGenType\r
121      *            generated type for which is the map of the necessary imports\r
122      *            built\r
123      * @param type\r
124      *            JAVA <code>Type</code> for which is the necessary of the\r
125      *            package import evaluated\r
126      * @param imports\r
127      *            map of the imports for <code>parentGenType</code>\r
128      * @throws IllegalArgumentException\r
129      *             <ul>\r
130      *             <li>if the <code>parentGenType</code> equals\r
131      *             <code>null</code></li>\r
132      *             <li>if the name of <code>parentGenType</code> equals\r
133      *             <code>null</code></li>\r
134      *             <li>if the name of the package of <code>parentGenType</code>\r
135      *             equals <code>null</code></li>\r
136      *             <li>if the <code>type</code> equals <code>null</code></li>\r
137      *             <li>if the name of <code>type</code> equals <code>null</code>\r
138      *             </li>\r
139      *             <li>if the name of the package of <code>type</code> equals\r
140      *             <code>null</code></li>\r
141      *             </ul>\r
142      */\r
143     public static void putTypeIntoImports(final GeneratedType parentGenType, final Type type,\r
144             final Map<String, String> imports) {\r
145         if (parentGenType == null) {\r
146             throw new IllegalArgumentException("Parent Generated Type parameter MUST be specified and cannot be "\r
147                     + "NULL!");\r
148         }\r
149         if (parentGenType.getName() == null) {\r
150             throw new IllegalArgumentException("Parent Generated Type name cannot be NULL!");\r
151         }\r
152         if (parentGenType.getPackageName() == null) {\r
153             throw new IllegalArgumentException("Parent Generated Type cannot have Package Name referenced as NULL!");\r
154         }\r
155         if (type == null) {\r
156             throw new IllegalArgumentException("Type parameter MUST be specified and cannot be NULL!");\r
157         }\r
158         if (type.getName() == null) {\r
159             throw new IllegalArgumentException("Type name cannot be NULL!");\r
160         }\r
161         if (type.getPackageName() == null) {\r
162             throw new IllegalArgumentException("Type cannot have Package Name referenced as NULL!");\r
163         }\r
164 \r
165         final String typeName = type.getName();\r
166         final String typePackageName = type.getPackageName();\r
167         final String parentTypeName = parentGenType.getName();\r
168         final String parentTypePackageName = parentGenType.getPackageName();\r
169         if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang")\r
170                 || typePackageName.equals(parentTypePackageName) || typePackageName.isEmpty()) {\r
171             return;\r
172         }\r
173         if (!imports.containsKey(typeName)) {\r
174             imports.put(typeName, typePackageName);\r
175         }\r
176         if (type instanceof ParameterizedType) {\r
177             final ParameterizedType paramType = (ParameterizedType) type;\r
178             final Type[] params = paramType.getActualTypeArguments();\r
179             for (Type param : params) {\r
180                 putTypeIntoImports(parentGenType, param, imports);\r
181             }\r
182         }\r
183     }\r
184 \r
185     /**\r
186      * Checks if the constant with the name <code>constName</code> is in the\r
187      * list of the constant definition for <code>genTO</code>.\r
188      * \r
189      * @param constName\r
190      *            string with the name of constant which is sought\r
191      * @param genTO\r
192      *            generated transfer object in which is <code>constName</code>\r
193      *            sought\r
194      * @return boolean value\r
195      *         <ul>\r
196      *         <li>true - if <code>constName</code> is in the list of the\r
197      *         constant definition for <code>genTO</code></li>\r
198      *         <li>false - in other cases</li>\r
199      *         </ul>\r
200      * @throws IllegalArgumentException\r
201      *             <ul>\r
202      *             <li>if <code>constName</code> equals <code>null</code></li>\r
203      *             <li>if <code>genTO</code> equals <code>null</code></li>\r
204      *             </ul>\r
205      */\r
206     public static boolean isConstantInTO(String constName, GeneratedTransferObject genTO) {\r
207         if (constName == null || genTO == null)\r
208             throw new IllegalArgumentException();\r
209         List<Constant> consts = genTO.getConstantDefinitions();\r
210         for (Constant cons : consts) {\r
211             if (cons.getName().equals(constName)) {\r
212                 return true;\r
213             }\r
214         }\r
215         return false;\r
216     }\r
217 \r
218     /**\r
219      * Creates the map which maps the type name to package name and contains\r
220      * only package names for enclosed types of <code>genType</code> and\r
221      * recursivelly their enclosed types.\r
222      * \r
223      * @param genType\r
224      *            JAVA <code>Type</code> for which is the map created\r
225      * @return map of the package names for all the enclosed types and\r
226      *         recursivelly their enclosed types\r
227      */\r
228     public static Map<String, String> createChildImports(GeneratedType genType) {\r
229         Map<String, String> childImports = new LinkedHashMap<>();\r
230         List<GeneratedType> childGeneratedTypes = genType.getEnclosedTypes();\r
231         if (!childGeneratedTypes.isEmpty()) {\r
232             for (GeneratedType genTypeChild : childGeneratedTypes) {\r
233                 createChildImports(genTypeChild);\r
234                 childImports.put(genTypeChild.getName(), genTypeChild.getPackageName());\r
235             }\r
236         }\r
237         return childImports;\r
238     }\r
239 \r
240     /**\r
241      * Builds the string which contains either the full path to the type\r
242      * (package name with type) or only type name if the package is among\r
243      * <code>imports</code>.\r
244      * \r
245      * @param parentGenType\r
246      *            generated type which contains <code>type</code>\r
247      * @param type\r
248      *            JAVA <code>Type</code> for which is the string with type info\r
249      *            generated\r
250      * @param imports\r
251      *            map of necessary imports for <code>parentGenType</code>\r
252      * @return string with type name for <code>type</code> in the full format or\r
253      *         in the short format\r
254      * @throws IllegalArgumentException\r
255      *             <ul>\r
256      *             <li>if the <code>type</code> equals <code>null</code></li>\r
257      *             <li>if the name of the <code>type</code> equals\r
258      *             <code>null</code></li>\r
259      *             <li>if the name of the package of the <code>type</code>\r
260      *             equals <code>null</code></li>\r
261      *             <li>if the <code>imports</code> equals <code>null</code></li>\r
262      *             </ul>\r
263      */\r
264     public static String getExplicitType(final GeneratedType parentGenType, final Type type,\r
265             final Map<String, String> imports) {\r
266         if (type == null) {\r
267             throw new IllegalArgumentException("Type parameter MUST be specified and cannot be NULL!");\r
268         }\r
269         if (type.getName() == null) {\r
270             throw new IllegalArgumentException("Type name cannot be NULL!");\r
271         }\r
272         if (type.getPackageName() == null) {\r
273             throw new IllegalArgumentException("Type cannot have Package Name referenced as NULL!");\r
274         }\r
275         if (imports == null) {\r
276             throw new IllegalArgumentException("Imports Map cannot be NULL!");\r
277         }\r
278 \r
279         final String typePackageName = type.getPackageName();\r
280         final String typeName = type.getName();\r
281         final String importedPackageName = imports.get(typeName);\r
282         if (typePackageName.equals(importedPackageName) || typePackageName.equals(parentGenType.getPackageName())) {\r
283             final StringBuilder builder = new StringBuilder(type.getName());\r
284             if (type instanceof ParameterizedType) {\r
285                 final ParameterizedType pType = (ParameterizedType) type;\r
286                 final Type[] pTypes = pType.getActualTypeArguments();\r
287                 builder.append("<");\r
288                 builder.append(getParameters(parentGenType, pTypes, imports));\r
289                 builder.append(">");\r
290             }\r
291             if (builder.toString().equals("Void")) {\r
292                 return "void";\r
293             }\r
294             return builder.toString();\r
295         } else {\r
296             final StringBuilder builder = new StringBuilder();\r
297             if (typePackageName.startsWith("java.lang")) {\r
298                 builder.append(type.getName());\r
299             } else {\r
300                 if (!typePackageName.isEmpty()) {\r
301                     builder.append(typePackageName + Constants.DOT + type.getName());\r
302                 } else {\r
303                     builder.append(type.getName());\r
304                 }\r
305             }\r
306             if (type.equals(Types.voidType())) {\r
307                 return "void";\r
308             }\r
309             if (type instanceof ParameterizedType) {\r
310                 final ParameterizedType pType = (ParameterizedType) type;\r
311                 final Type[] pTypes = pType.getActualTypeArguments();\r
312                 builder.append("<");\r
313                 builder.append(getParameters(parentGenType, pTypes, imports));\r
314                 builder.append(">");\r
315             }\r
316             return builder.toString();\r
317         }\r
318     }\r
319 \r
320     /**\r
321      * Generates the string with all actual type parameters from\r
322      * <code>pTypes</code>\r
323      * \r
324      * @param parentGenType\r
325      *            generated type for which is the JAVA code generated\r
326      * @param pTypes\r
327      *            array of <code>Type</code> instances = actual type parameters\r
328      * @param availableImports\r
329      *            map of imports for <code>parentGenType</code>\r
330      * @return string with all actual type parameters from <code>pTypes</code>\r
331      */\r
332     private static String getParameters(final GeneratedType parentGenType, final Type[] pTypes,\r
333             Map<String, String> availableImports) {\r
334         final StringBuilder builder = new StringBuilder();\r
335         for (int i = 0; i < pTypes.length; i++) {\r
336             final Type t = pTypes[i];\r
337 \r
338             String separator = COMMA;\r
339             if (i == (pTypes.length - 1)) {\r
340                 separator = "";\r
341             }\r
342 \r
343             String wildcardParam = "";\r
344             if (t.equals(Types.voidType())) {\r
345                 builder.append("java.lang.Void" + separator);\r
346                 continue;\r
347             } else {\r
348 \r
349                 if (t instanceof WildcardType) {\r
350                     wildcardParam = "? extends ";\r
351                 }\r
352 \r
353                 builder.append(wildcardParam + getExplicitType(parentGenType, t, availableImports) + separator);\r
354             }\r
355         }\r
356         return builder.toString();\r
357     }\r
358 \r
359     /**\r
360      * Returns the reference to highest (top parent) Generated Transfer Object.\r
361      * \r
362      * @param childTransportObject\r
363      *            is generated transfer object which can be extended by other\r
364      *            generated transfer object\r
365      * @return in first case that <code>childTransportObject</code> isn't\r
366      *         extended then <code>childTransportObject</code> is returned. In\r
367      *         second case the method is recursive called until first case.\r
368      * @throws IllegalArgumentException\r
369      *             if <code>childTransportObject</code> equals <code>null</code>\r
370      */\r
371     public static GeneratedTransferObject getTopParrentTransportObject(GeneratedTransferObject childTransportObject) {\r
372         if (childTransportObject == null) {\r
373             throw new IllegalArgumentException("Parameter childTransportObject can't be null.");\r
374         }\r
375         if (childTransportObject.getExtends() == null) {\r
376             return childTransportObject;\r
377         } else {\r
378             return getTopParrentTransportObject(childTransportObject.getExtends());\r
379         }\r
380     }\r
381 \r
382     /**\r
383      * Selects from input list of properties only those which have read only\r
384      * attribute set to true.\r
385      * \r
386      * @param properties\r
387      *            list of properties of generated transfer object\r
388      * @return subset of <code>properties</code> which have read only attribute\r
389      *         set to true\r
390      */\r
391     public static List<GeneratedProperty> resolveReadOnlyPropertiesFromTO(List<GeneratedProperty> properties) {\r
392         List<GeneratedProperty> readOnlyProperties = new ArrayList<GeneratedProperty>();\r
393         if (properties != null) {\r
394             for (final GeneratedProperty property : properties) {\r
395                 if (property.isReadOnly()) {\r
396                     readOnlyProperties.add(property);\r
397                 }\r
398             }\r
399         }\r
400         return readOnlyProperties;\r
401     }\r
402 \r
403     /**\r
404      * Returns the list of the read only properties of all extending generated\r
405      * transfer object from <code>genTO</code> to highest parent generated\r
406      * transfer object\r
407      * \r
408      * @param genTO\r
409      *            generated transfer object for which is the list of read only\r
410      *            properties generated\r
411      * @return list of all read only properties from actual to highest parent\r
412      *         generated transfer object. In case when extension exists the\r
413      *         method is recursive called.\r
414      */\r
415     public static List<GeneratedProperty> getPropertiesOfAllParents(GeneratedTransferObject genTO) {\r
416         List<GeneratedProperty> propertiesOfAllParents = new ArrayList<GeneratedProperty>();\r
417         if (genTO.getExtends() != null) {\r
418             final List<GeneratedProperty> allPropertiesOfTO = genTO.getExtends().getProperties();\r
419             List<GeneratedProperty> readOnlyPropertiesOfTO = resolveReadOnlyPropertiesFromTO(allPropertiesOfTO);\r
420             propertiesOfAllParents.addAll(readOnlyPropertiesOfTO);\r
421             propertiesOfAllParents.addAll(getPropertiesOfAllParents(genTO.getExtends()));\r
422         }\r
423         return propertiesOfAllParents;\r
424     }\r
425 \r
426 }\r