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