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