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