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