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