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