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