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