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