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