Fixing sonar issues
[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 import com.google.common.base.Preconditions;
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     }
36
37     /**
38      * Returns the map of imports. The map maps the type name to the package
39      * name. To the map are added packages for <code>genType</code> and for all
40      * enclosed types, constants, methods (parameter types, return values),
41      * implemented types.
42      * 
43      * @param genType
44      *            generated type for which the map of the imports is created
45      * @return map of the necessary imports
46      * @throws IllegalArgumentException
47      *             if <code>genType</code> equals <code>null</code>
48      */
49     public static Map<String, String> createImports(GeneratedType genType) {
50         if (genType == null) {
51             throw new IllegalArgumentException("Generated Type cannot be NULL!");
52         }
53         final Map<String, String> imports = new LinkedHashMap<>();
54
55         List<GeneratedType> childGeneratedTypes = genType.getEnclosedTypes();
56         if (!childGeneratedTypes.isEmpty()) {
57             for (GeneratedType genTypeChild : childGeneratedTypes) {
58                 imports.putAll(createImports(genTypeChild));
59             }
60         }
61
62         final List<Constant> constants = genType.getConstantDefinitions();
63         final List<MethodSignature> methods = genType.getMethodDefinitions();
64         final List<Type> impl = genType.getImplements();
65
66         // IMPLEMENTATIONS
67         if (impl != null) {
68             for (final Type type : impl) {
69                 putTypeIntoImports(genType, type, imports);
70             }
71         }
72
73         // CONSTANTS
74         if (constants != null) {
75             for (final Constant constant : constants) {
76                 final Type constantType = constant.getType();
77                 putTypeIntoImports(genType, constantType, imports);
78             }
79         }
80
81         // REGULAR EXPRESSION
82         if (genType instanceof GeneratedTransferObject
83                 && isConstantInTO(TypeConstants.PATTERN_CONSTANT_NAME, (GeneratedTransferObject) genType)) {
84             putTypeIntoImports(genType, Types.typeForClass(java.util.regex.Pattern.class), imports);
85             putTypeIntoImports(genType, Types.typeForClass(java.util.Arrays.class), imports);
86             putTypeIntoImports(genType, Types.typeForClass(java.util.ArrayList.class), imports);
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         Preconditions.checkArgument(parentGenType != null,
146                 "Parent Generated Type parameter MUST be specified and cannot be " + "NULL!");
147         Preconditions.checkArgument(parentGenType.getName() != null, "Parent Generated Type name cannot be NULL!");
148         Preconditions.checkArgument(parentGenType.getPackageName() != null,
149                 "Parent Generated Type cannot have Package Name referenced as NULL!");
150         Preconditions.checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
151
152         Preconditions.checkArgument(type.getName() != null, "Type name cannot be NULL!");
153         Preconditions.checkArgument(type.getPackageName() != null, "Type cannot have Package Name referenced as NULL!");
154
155         final String typeName = type.getName();
156         final String typePackageName = type.getPackageName();
157         final String parentTypeName = parentGenType.getName();
158         final String parentTypePackageName = parentGenType.getPackageName();
159         if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang")
160                 || typePackageName.equals(parentTypePackageName) || typePackageName.isEmpty()) {
161             return;
162         }
163         if (!imports.containsKey(typeName)) {
164             imports.put(typeName, typePackageName);
165         }
166         if (type instanceof ParameterizedType) {
167             final ParameterizedType paramType = (ParameterizedType) type;
168             final Type[] params = paramType.getActualTypeArguments();
169             for (Type param : params) {
170                 putTypeIntoImports(parentGenType, param, imports);
171             }
172         }
173     }
174
175     /**
176      * Checks if the constant with the name <code>constName</code> is in the
177      * list of the constant definition for <code>genTO</code>.
178      * 
179      * @param constName
180      *            string with the name of constant which is sought
181      * @param genTO
182      *            generated transfer object in which is <code>constName</code>
183      *            sought
184      * @return boolean value
185      *         <ul>
186      *         <li>true - if <code>constName</code> is in the list of the
187      *         constant definition for <code>genTO</code></li>
188      *         <li>false - in other cases</li>
189      *         </ul>
190      * @throws IllegalArgumentException
191      *             <ul>
192      *             <li>if <code>constName</code> equals <code>null</code></li>
193      *             <li>if <code>genTO</code> equals <code>null</code></li>
194      *             </ul>
195      */
196     public static boolean isConstantInTO(String constName, GeneratedTransferObject genTO) {
197         if (constName == null || genTO == null)
198             throw new IllegalArgumentException();
199         List<Constant> consts = genTO.getConstantDefinitions();
200         for (Constant cons : consts) {
201             if (cons.getName().equals(constName)) {
202                 return true;
203             }
204         }
205         return false;
206     }
207
208     /**
209      * Creates the map which maps the type name to package name and contains
210      * only package names for enclosed types of <code>genType</code> and
211      * recursivelly their enclosed types.
212      * 
213      * @param genType
214      *            JAVA <code>Type</code> for which is the map created
215      * @return map of the package names for all the enclosed types and
216      *         recursivelly their enclosed types
217      */
218     public static Map<String, String> createChildImports(GeneratedType genType) {
219         Map<String, String> childImports = new LinkedHashMap<>();
220         List<GeneratedType> childGeneratedTypes = genType.getEnclosedTypes();
221         if (!childGeneratedTypes.isEmpty()) {
222             for (GeneratedType genTypeChild : childGeneratedTypes) {
223                 createChildImports(genTypeChild);
224                 childImports.put(genTypeChild.getName(), genTypeChild.getPackageName());
225             }
226         }
227         return childImports;
228     }
229
230     /**
231      * Builds the string which contains either the full path to the type
232      * (package name with type) or only type name if the package is among
233      * <code>imports</code>.
234      * 
235      * @param parentGenType
236      *            generated type which contains <code>type</code>
237      * @param type
238      *            JAVA <code>Type</code> for which is the string with type info
239      *            generated
240      * @param imports
241      *            map of necessary imports for <code>parentGenType</code>
242      * @return string with type name for <code>type</code> in the full format or
243      *         in the short format
244      * @throws IllegalArgumentException
245      *             <ul>
246      *             <li>if the <code>type</code> equals <code>null</code></li>
247      *             <li>if the name of the <code>type</code> equals
248      *             <code>null</code></li>
249      *             <li>if the name of the package of the <code>type</code>
250      *             equals <code>null</code></li>
251      *             <li>if the <code>imports</code> equals <code>null</code></li>
252      *             </ul>
253      */
254     public static String getExplicitType(final GeneratedType parentGenType, final Type type,
255             final Map<String, String> imports) {
256
257         Preconditions.checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
258         Preconditions.checkArgument(type.getName() != null, "Type name cannot be NULL!");
259         Preconditions.checkArgument(type.getPackageName() != null, "Type cannot have Package Name referenced as NULL!");
260         Preconditions.checkArgument(imports != null, "Imports Map cannot be NULL!");
261
262         final String typePackageName = type.getPackageName();
263         final String typeName = type.getName();
264         final String importedPackageName = imports.get(typeName);
265         final StringBuilder builder;
266         if (typePackageName.equals(importedPackageName) || typePackageName.equals(parentGenType.getPackageName())) {
267             builder = new StringBuilder(type.getName());
268             addActualTypeParameters(builder, type, parentGenType, imports);
269             if (builder.toString().equals("Void")) {
270                 return "void";
271             }
272         } else {
273             builder = new StringBuilder();
274             if (typePackageName.startsWith("java.lang")) {
275                 builder.append(type.getName());
276             } else {
277                 if (!typePackageName.isEmpty()) {
278                     builder.append(typePackageName + Constants.DOT + type.getName());
279                 } else {
280                     builder.append(type.getName());
281                 }
282             }
283             if (type.equals(Types.voidType())) {
284                 return "void";
285             }
286             addActualTypeParameters(builder, type, parentGenType, imports);
287         }
288         return builder.toString();
289
290     }
291
292     /**
293      * Adds actual type parameters from <code>type</code> to
294      * <code>builder</code> if <code>type</code> is
295      * <code>ParametrizedType</code>.
296      * 
297      * @param builder
298      *            string builder which contains type name
299      * @param type
300      *            JAVA <code>Type</code> for which is the string with type info
301      *            generated
302      * @param parentGenType
303      *            generated type which contains <code>type</code>
304      * @param imports
305      *            map of necessary imports for <code>parentGenType</code>
306      * @return if <code>type</code> is of the type <code>ParametrizedType</code> <br />
307      *         <li>then <code>builder</code> + actual <code>type</code>
308      *         parameters</li> <li>else only <code>builder</code></li>
309      */
310     private static StringBuilder addActualTypeParameters(StringBuilder builder, final Type type,
311             final GeneratedType parentGenType, final Map<String, String> imports) {
312         if (type instanceof ParameterizedType) {
313             final ParameterizedType pType = (ParameterizedType) type;
314             final Type[] pTypes = pType.getActualTypeArguments();
315             builder.append("<");
316             builder.append(getParameters(parentGenType, pTypes, imports));
317             builder.append(">");
318         }
319         return builder;
320     }
321
322     /**
323      * Generates the string with all actual type parameters from
324      * <code>pTypes</code>
325      * 
326      * @param parentGenType
327      *            generated type for which is the JAVA code generated
328      * @param pTypes
329      *            array of <code>Type</code> instances = actual type parameters
330      * @param availableImports
331      *            map of imports for <code>parentGenType</code>
332      * @return string with all actual type parameters from <code>pTypes</code>
333      */
334     private static String getParameters(final GeneratedType parentGenType, final Type[] pTypes,
335             Map<String, String> availableImports) {
336         final StringBuilder builder = new StringBuilder();
337         for (int i = 0; i < pTypes.length; i++) {
338             final Type t = pTypes[i];
339
340             String separator = COMMA;
341             if (i == (pTypes.length - 1)) {
342                 separator = "";
343             }
344
345             String wildcardParam = "";
346             if (t.equals(Types.voidType())) {
347                 builder.append("java.lang.Void" + separator);
348                 continue;
349             } else {
350
351                 if (t instanceof WildcardType) {
352                     wildcardParam = "? extends ";
353                 }
354
355                 builder.append(wildcardParam + getExplicitType(parentGenType, t, availableImports) + separator);
356             }
357         }
358         return builder.toString();
359     }
360
361     /**
362      * Returns the reference to highest (top parent) Generated Transfer Object.
363      * 
364      * @param childTransportObject
365      *            is generated transfer object which can be extended by other
366      *            generated transfer object
367      * @return in first case that <code>childTransportObject</code> isn't
368      *         extended then <code>childTransportObject</code> is returned. In
369      *         second case the method is recursive called until first case.
370      * @throws IllegalArgumentException
371      *             if <code>childTransportObject</code> equals <code>null</code>
372      */
373     public static GeneratedTransferObject getTopParrentTransportObject(GeneratedTransferObject childTransportObject) {
374         if (childTransportObject == null) {
375             throw new IllegalArgumentException("Parameter childTransportObject can't be null.");
376         }
377         if (childTransportObject.getSuperType() == null) {
378             return childTransportObject;
379         } else {
380             return getTopParrentTransportObject(childTransportObject.getSuperType());
381         }
382     }
383
384     /**
385      * Selects from input list of properties only those which have read only
386      * attribute set to true.
387      * 
388      * @param properties
389      *            list of properties of generated transfer object
390      * @return subset of <code>properties</code> which have read only attribute
391      *         set to true
392      */
393     public static List<GeneratedProperty> resolveReadOnlyPropertiesFromTO(List<GeneratedProperty> properties) {
394         List<GeneratedProperty> readOnlyProperties = new ArrayList<GeneratedProperty>();
395         if (properties != null) {
396             for (final GeneratedProperty property : properties) {
397                 if (property.isReadOnly()) {
398                     readOnlyProperties.add(property);
399                 }
400             }
401         }
402         return readOnlyProperties;
403     }
404
405     /**
406      * Returns the list of the read only properties of all extending generated
407      * transfer object from <code>genTO</code> to highest parent generated
408      * transfer object
409      * 
410      * @param genTO
411      *            generated transfer object for which is the list of read only
412      *            properties generated
413      * @return list of all read only properties from actual to highest parent
414      *         generated transfer object. In case when extension exists the
415      *         method is recursive called.
416      */
417     public static List<GeneratedProperty> getPropertiesOfAllParents(GeneratedTransferObject genTO) {
418         List<GeneratedProperty> propertiesOfAllParents = new ArrayList<GeneratedProperty>();
419         if (genTO.getSuperType() != null) {
420             final List<GeneratedProperty> allPropertiesOfTO = genTO.getSuperType().getProperties();
421             List<GeneratedProperty> readOnlyPropertiesOfTO = resolveReadOnlyPropertiesFromTO(allPropertiesOfTO);
422             propertiesOfAllParents.addAll(readOnlyPropertiesOfTO);
423             propertiesOfAllParents.addAll(getPropertiesOfAllParents(genTO.getSuperType()));
424         }
425         return propertiesOfAllParents;
426     }
427
428 }