Switch to Objects.requireNonNull
[mdsal.git] / binding2 / mdsal-binding2-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / javav2 / java / api / generator / renderers / BaseRenderer.java
1 /*
2  * Copyright (c) 2017 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
9 package org.opendaylight.mdsal.binding.javav2.java.api.generator.renderers;
10
11 import static com.google.common.base.Preconditions.checkArgument;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.collect.ImmutableMap;
15
16 import java.util.Collection;
17 import java.util.HashMap;
18 import java.util.LinkedList;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.mdsal.binding.javav2.generator.util.Types;
24 import org.opendaylight.mdsal.binding.javav2.java.api.generator.util.TextTemplateUtil;
25 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedProperty;
26 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedTransferObject;
27 import org.opendaylight.mdsal.binding.javav2.model.api.GeneratedType;
28 import org.opendaylight.mdsal.binding.javav2.model.api.MethodSignature;
29 import org.opendaylight.mdsal.binding.javav2.model.api.ParameterizedType;
30 import org.opendaylight.mdsal.binding.javav2.model.api.Type;
31 import org.opendaylight.mdsal.binding.javav2.model.api.WildcardType;
32
33
34
35 public abstract class BaseRenderer {
36     private static final String COMMA = ",";
37     private static final String DOT = ".";
38
39     private final GeneratedType genType;
40     private final Map<String, String> importMap;
41     /**
42      * list of all imported names for template.
43      */
44     private final Map<String, String> importedNames = new HashMap<>();
45
46     protected BaseRenderer(final GeneratedType type) {
47         this.genType = requireNonNull(type);
48         this.importMap = new HashMap<>();
49     }
50
51     /**
52      * Implementation needs to call Scala template render() method to generate string body.
53      * @return rendered body
54      */
55     protected abstract String body();
56
57     protected GeneratedType getType() {
58         return genType;
59     }
60
61     protected Map<String, String> getImportedNames() {
62         return importedNames;
63     }
64
65     protected String getFromImportMap(@NonNull final String typeName) {
66         return importMap.get(typeName);
67     }
68
69     protected void putToImportMap(@NonNull final String typeName, final String typePackageName) {
70         importMap.put(typeName, typePackageName);
71     }
72
73     protected void putAllToImportMap(@NonNull final Map<String, String> imports) {
74         importMap.putAll(imports);
75     }
76
77     protected Map<String, String> getImportMap() {
78         return ImmutableMap.copyOf(importMap);
79     }
80
81     /**
82      * Return string with type name for type in the full format or in the short format.
83      * @param intype type to format and add to imports
84      * @return formatted type
85      */
86     protected String importedName(final Type intype) {
87         putTypeIntoImports(genType, intype);
88         return getExplicitType(genType, intype);
89     }
90
91     protected String importedName(final Class<?> cls) {
92         return importedName(Types.typeForClass(cls));
93     }
94
95     /**
96      * Generate package definition for template.
97      * @return package definition for template
98      */
99     protected String packageDefinition() {
100         final StringBuilder sb = new StringBuilder();
101         sb.append("package ")
102                 .append(genType.getPackageName())
103                 .append(";\n\n");
104         return sb.toString();
105     }
106
107     /**
108      * walks through map of imports.
109      * @return string of imports for template
110      */
111     private String imports() {
112         final StringBuilder sb = new StringBuilder();
113         if (!importMap.isEmpty()) {
114             for (Map.Entry<String, String> entry : importMap.entrySet()) {
115                 if (!hasSamePackage(entry.getValue())) {
116                     sb.append("import ")
117                             .append(entry.getValue())
118                             .append('.')
119                             .append(entry.getKey())
120                             .append(";\n");
121                 }
122             }
123         }
124         return sb.toString();
125     }
126
127     /**
128      * Template method which generates method parameters with their types from <code>parameters</code>.
129      *
130      * @param parameters
131      * group of generated property instances which are transformed to the method parameters
132      * @return string with the list of the method parameters with their types in JAVA format
133      */
134     protected String asArgumentsDeclaration(final Collection<GeneratedProperty> parameters) {
135         final List<CharSequence> strings = new LinkedList<>();
136         if (parameters.iterator().hasNext()) {
137             for (GeneratedProperty parameter : parameters) {
138                 final StringBuilder sb = new StringBuilder();
139                 sb.append(importedName(parameter.getReturnType()));
140                 sb.append(' ');
141                 sb.append(TextTemplateUtil.fieldName(parameter));
142                 strings.add(sb);
143             }
144         }
145         return String.join(", ", strings);
146     }
147
148     /**
149      * Checks if package of generated type and imported type is the same.
150      * @param importedTypePackageName imported types package name
151      * @return equals packages
152      */
153     protected boolean hasSamePackage(final String importedTypePackageName) {
154         return genType.getPackageName().equals(importedTypePackageName);
155     }
156
157     /**
158      * Evaluates if it is necessary to add the package name for type to the map of imports for parentGenType
159      * If it is so the package name is saved to the map imports.
160      *
161      * @param parentGenType generated type for which is the map of necessary imports build
162      * @param type JAVA type for which is the necessary of the package import evaluated
163      */
164     private void putTypeIntoImports(final GeneratedType parentGenType, final Type type) {
165         checkArgument(parentGenType != null, "Parent Generated Type parameter MUST be specified and cannot be NULL!");
166         checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
167         checkArgument(parentGenType.getPackageName() != null,
168                 "Parent Generated Type cannot have Package Name referenced as NULL!");
169
170         final String typeName = requireNonNull(type.getName());
171         final String typePackageName = requireNonNull(type.getPackageName());
172         final String parentTypeName = requireNonNull(parentGenType.getName());
173         if (typeName.equals(parentTypeName) || typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
174             return;
175         }
176         if (!importMap.containsKey(typeName)) {
177             importMap.put(typeName, typePackageName);
178         }
179         if (type instanceof ParameterizedType) {
180             final ParameterizedType paramType = (ParameterizedType) type;
181             final Type[] params = paramType.getActualTypeArguments();
182             if (params != null) {
183                 for (Type param : params) {
184                     putTypeIntoImports(parentGenType, param);
185                 }
186             }
187         }
188     }
189
190     /**
191      * Builds the string which contains either the full path to the type (package name with type) or only type name
192      * if the package is among imports.
193      *
194      * @param parentGenType generated type which contains type
195      * @param type JAVA type for which is the string with type info generated
196      * @return string with type name for type in the full format or in the short format
197      */
198     private String getExplicitType(final GeneratedType parentGenType, final Type type) {
199         checkArgument(type != null, "Type parameter MUST be specified and cannot be NULL!");
200         checkArgument(importMap != null, "Imports Map cannot be NULL!");
201
202         final String typePackageName = requireNonNull(type.getPackageName());
203         final String typeName = requireNonNull(type.getName());
204         final String importedPackageName = importMap.get(typeName);
205         final StringBuilder sb;
206         if (typePackageName.equals(importedPackageName)) {
207             sb = new StringBuilder(typeName);
208             addActualTypeParameters(sb, type, parentGenType);
209             if (sb.toString().equals("Void")) {
210                 return "void";
211             }
212         } else {
213             sb = new StringBuilder();
214             if (!typePackageName.isEmpty()) {
215                 sb.append(typePackageName).append(DOT).append(typeName);
216             } else {
217                 sb.append(type.getName());
218             }
219             if (type.equals(Types.voidType())) {
220                 return "void";
221             }
222             addActualTypeParameters(sb, type, parentGenType);
223         }
224         return sb.toString();
225     }
226
227     /**
228      * Adds actual type parameters from type to builder if type is ParametrizedType.
229      *
230      * @param sb string builder which contains type name
231      * @param type JAVA Type for which is the string with type info generated
232      * @param parentGenType generated type which contains type
233      * @return adds actual type parameters to builder
234      */
235     private StringBuilder addActualTypeParameters(final StringBuilder sb, final Type type,
236             final GeneratedType parentGenType) {
237         if (type instanceof ParameterizedType) {
238             final ParameterizedType pType = (ParameterizedType) type;
239             final Type[] pTypes = pType.getActualTypeArguments();
240             sb.append('<');
241             sb.append(getParameters(parentGenType, pTypes));
242             sb.append('>');
243         }
244         return sb;
245     }
246
247     protected GeneratedProperty findProperty(final GeneratedTransferObject gto, final String name) {
248         for (GeneratedProperty prop : gto.getProperties()) {
249             if (name.equals(prop.getName())) {
250                 return prop;
251             }
252         }
253         final GeneratedTransferObject parent = gto.getSuperType();
254         if (parent != null) {
255             return findProperty(parent, name);
256         }
257         return null;
258     }
259
260     /**
261      * Generates the string with all actual type parameters from.
262      *
263      * @param parentGenType generated type for which is the JAVA code generated
264      * @param types array of Type instances = actual type parameters
265      * @return string with all actual type parameters from types
266      */
267     private String getParameters(final GeneratedType parentGenType, final Type[] types) {
268         if (types == null || types.length == 0) {
269             return "?";
270         }
271         final StringBuilder sb = new StringBuilder();
272         for (int i = 0; i < types.length; i++) {
273             final Type t = types[i];
274
275             String separator = COMMA;
276             if (i == types.length - 1) {
277                 separator = "";
278             }
279
280
281             if (t.equals(Types.voidType())) {
282                 sb.append("java.lang.Void")
283                         .append(separator);
284                 continue;
285             } else {
286
287                 String wildcardParam = "";
288                 if (t instanceof WildcardType) {
289                     wildcardParam = "? extends ";
290                 }
291
292                 sb.append(wildcardParam).append(getExplicitType(parentGenType, t)).append(separator);
293             }
294         }
295         return sb.toString();
296     }
297
298     /**
299      * Template method which generates method parameters with their types from <code>parameters</code>.
300      * InterfaceTemplate / UnionTemaplate
301      *
302      * @param parameters list of parameter instances which are transformed to the method parameters
303      * @return string with the list of the method parameters with their types in JAVA format
304      */
305     protected String generateParameters(final List<MethodSignature.Parameter> parameters) {
306         final List<CharSequence> strings = new LinkedList<>();
307         if (!parameters.isEmpty()) {
308             for (MethodSignature.Parameter parameter : parameters) {
309                 final StringBuilder sb = new StringBuilder();
310                 sb.append(importedName(parameter.getType()));
311                 sb.append(' ');
312                 sb.append(parameter.getName());
313                 strings.add(sb);
314             }
315         }
316         return String.join(", ", strings);
317     }
318
319     /**
320      * Template method which generates the getter method for field.
321      *
322      * @param field generated property with data about field which is generated as the getter method
323      * @return string with the getter method source code in JAVA format
324      */
325     protected String getterMethod(final GeneratedProperty field) {
326         final StringBuilder sb = new StringBuilder();
327         final String name = TextTemplateUtil.fieldName(field);
328         final String importedName = requireNonNull(importedName(field.getReturnType()));
329         sb.append("public ")
330                 .append(importedName)
331                 .append(' ')
332                 .append(TextTemplateUtil.getterMethodName(field))
333                 .append("() {")
334                 .append("return ")
335                 .append(name);
336         if (!(field.getReturnType() instanceof ParameterizedType)
337                 && importedName.contains("[]")) {
338             sb.append(" == null ? null : ")
339                     .append(name)
340                     .append(".clone()");
341         }
342         sb.append(";}\n");
343         return sb.toString();
344     }
345
346     /**
347      * builds template.
348      * @return generated final template
349      */
350     public String generateTemplate() {
351         final StringBuilder sb = new StringBuilder();
352         /* sb body must be filled before imports method call */
353         final String templateBody = body();
354         sb.append(packageDefinition())
355                 .append(imports())
356                 .append(templateBody);
357         return sb.toString();
358     }
359 }