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