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