Bug 6859: Binding generator v1 refactoring
[mdsal.git] / binding / mdsal-binding-java-api-generator / src / main / java / org / opendaylight / mdsal / binding / java / api / generator / YangModuleInfoTemplate.xtend
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 org.opendaylight.yangtools.yang.binding.BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME
11 import static org.opendaylight.yangtools.yang.binding.BindingMapping.MODULE_INFO_CLASS_NAME
12 import static org.opendaylight.yangtools.yang.binding.BindingMapping.getClassName
13 import static org.opendaylight.yangtools.yang.binding.BindingMapping.getRootPackageName
14
15 import com.google.common.base.Preconditions
16 import com.google.common.collect.ImmutableSet
17 import java.io.IOException
18 import java.io.InputStream
19 import java.text.DateFormat
20 import java.text.SimpleDateFormat
21 import java.util.Collections
22 import java.util.Date
23 import java.util.HashSet
24 import java.util.LinkedHashMap
25 import java.util.Map
26 import java.util.Optional
27 import java.util.Set
28 import java.util.TreeMap
29 import java.util.function.Function
30 import org.eclipse.xtend.lib.Property
31 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
32 import org.opendaylight.mdsal.binding.model.api.Type
33 import org.opendaylight.mdsal.binding.model.api.WildcardType
34 import org.opendaylight.mdsal.binding.model.util.Types
35 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
36 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
37 import org.opendaylight.yangtools.yang.model.api.Module
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext
39
40 class YangModuleInfoTemplate {
41
42     val Module module
43     val SchemaContext ctx
44     val Map<String, String> importMap = new LinkedHashMap()
45     val Function<Module, Optional<String>> moduleFilePathResolver
46
47     @Property
48     val String packageName;
49
50     @Property
51     val String modelBindingProviderName;
52
53     new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
54         Preconditions.checkArgument(module !== null, "Module must not be null.");
55         this.module = module
56         this.ctx = ctx
57         this.moduleFilePathResolver = moduleFilePathResolver
58         _packageName = getRootPackageName(module.getQNameModule());
59         _modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»''';
60     }
61
62     def String generate() {
63         val body = '''
64             public final class «MODULE_INFO_CLASS_NAME» implements «YangModuleInfo.importedName» {
65
66                 private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
67
68                 private final «String.importedName» name = "«module.name»";
69                 private final «String.importedName» namespace = "«module.namespace.toString»";
70                 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
71                 private final «String.importedName» revision = "«df.format(module.revision)»";
72                 private final «String.importedName» resourcePath = "«sourcePath(module)»";
73
74                 private final «Set.importedName»<YangModuleInfo> importedModules;
75
76                 public static «YangModuleInfo.importedName» getInstance() {
77                     return INSTANCE;
78                 }
79
80                 «classBody(module, MODULE_INFO_CLASS_NAME)»
81             }
82         '''
83         return '''
84             package «packageName» ;
85             «imports»
86             «body»
87         '''.toString
88     }
89
90     def String generateModelProvider() {
91         '''
92             package «packageName»;
93
94             public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
95
96                 public «YangModuleInfo.name» getModuleInfo() {
97                     return «MODULE_INFO_CLASS_NAME».getInstance();
98                 }
99             }
100         '''
101
102     }
103
104     private def CharSequence classBody(Module m, String className) '''
105         private «className»() {
106             «IF !m.imports.empty || !m.submodules.empty»
107                 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
108             «ENDIF»
109             «IF !m.imports.empty»
110                 «FOR imp : m.imports»
111                     «val name = imp.moduleName»
112                     «val rev = imp.revision»
113                     «IF rev === null»
114                         «val Set<Module> modules = ctx.modules»
115                         «val TreeMap<Date, Module> sorted = new TreeMap()»
116                         «FOR module : modules»
117                             «IF module.name.equals(name)»
118                                 «sorted.put(module.revision, module)»
119                             «ENDIF»
120                         «ENDFOR»
121                         set.add(«getRootPackageName(sorted.lastEntry().value.QNameModule)».«MODULE_INFO_CLASS_NAME».getInstance());
122                     «ELSE»
123                         set.add(«getRootPackageName((ctx.findModuleByName(name, rev).QNameModule))».«MODULE_INFO_CLASS_NAME».getInstance());
124                     «ENDIF»
125                 «ENDFOR»
126             «ENDIF»
127             «IF !m.submodules.empty»
128                 «FOR submodule : m.submodules»
129                     set.add(«getClassName(submodule.name)»Info.getInstance());
130                 «ENDFOR»
131             «ENDIF»
132             «IF m.imports.empty && m.submodules.empty»
133                 importedModules = «Collections.importedName».emptySet();
134             «ELSE»
135                 importedModules = «ImmutableSet.importedName».copyOf(set);
136             «ENDIF»
137
138             «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
139             if (stream == null) {
140                 throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
141             }
142             try {
143                 stream.close();
144             } catch («IOException.importedName» e) {
145             // Resource leak, but there is nothing we can do
146             }
147         }
148
149         @Override
150         public «String.importedName» getName() {
151             return name;
152         }
153
154         @Override
155         public «String.importedName» getRevision() {
156             return revision;
157         }
158
159         @Override
160         public «String.importedName» getNamespace() {
161             return namespace;
162         }
163
164         @Override
165         public «InputStream.importedName» getModuleSourceStream() throws IOException {
166             «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
167             if (stream == null) {
168                 throw new «IOException.importedName»("Resource " + resourcePath + " is missing");
169             }
170             return stream;
171         }
172
173         @Override
174         public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
175             return importedModules;
176         }
177
178         @Override
179         public «String.importedName» toString() {
180             «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
181             sb.append("[");
182             sb.append("name = " + name);
183             sb.append(", namespace = " + namespace);
184             sb.append(", revision = " + revision);
185             sb.append(", resourcePath = " + resourcePath);
186             sb.append(", imports = " + importedModules);
187             sb.append("]");
188             return sb.toString();
189         }
190
191         «generateSubInfo(m)»
192
193     '''
194
195     private def sourcePath(Module module) {
196         val opt = moduleFilePathResolver.apply(module)
197         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
198         return opt.get
199     }
200
201     private def imports() '''
202         «IF !importMap.empty»
203             «FOR entry : importMap.entrySet»
204                 «IF entry.value != getRootPackageName(module.QNameModule)»
205                     import «entry.value».«entry.key»;
206                 «ENDIF»
207             «ENDFOR»
208         «ENDIF»
209     '''
210
211     final protected def importedName(Class<?> cls) {
212         val Type intype = Types.typeForClass(cls)
213         putTypeIntoImports(intype);
214         getExplicitType(intype)
215     }
216
217     final def void putTypeIntoImports(Type type) {
218         val String typeName = type.getName();
219         val String typePackageName = type.getPackageName();
220         if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
221             return;
222         }
223         if (!importMap.containsKey(typeName)) {
224             importMap.put(typeName, typePackageName);
225         }
226         if (type instanceof ParameterizedType) {
227             val Type[] params = type.getActualTypeArguments()
228             if (params !== null) {
229                 for (Type param : params) {
230                     putTypeIntoImports(param);
231                 }
232             }
233         }
234     }
235
236     final def String getExplicitType(Type type) {
237         val String typePackageName = type.getPackageName();
238         val String typeName = type.getName();
239         val String importedPackageName = importMap.get(typeName);
240         var StringBuilder builder;
241         if (typePackageName.equals(importedPackageName)) {
242             builder = new StringBuilder(type.getName());
243             if (builder.toString().equals("Void")) {
244                 return "void";
245             }
246             addActualTypeParameters(builder, type);
247         } else {
248             if (type.equals(Types.voidType())) {
249                 return "void";
250             }
251             builder = new StringBuilder();
252             if (!typePackageName.isEmpty()) {
253                 builder.append(typePackageName + Constants.DOT + type.getName());
254             } else {
255                 builder.append(type.getName());
256             }
257             addActualTypeParameters(builder, type);
258         }
259         return builder.toString();
260     }
261
262     final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
263         if (type instanceof ParameterizedType) {
264             val Type[] pTypes = type.getActualTypeArguments();
265             builder.append('<');
266             builder.append(getParameters(pTypes));
267             builder.append('>');
268         }
269         return builder;
270     }
271
272     final def String getParameters(Type[] pTypes) {
273         if (pTypes === null || pTypes.length == 0) {
274             return "?";
275         }
276         val StringBuilder builder = new StringBuilder();
277
278         var int i = 0;
279         for (pType : pTypes) {
280             val Type t = pTypes.get(i)
281
282             var String separator = ",";
283             if (i == (pTypes.length - 1)) {
284                 separator = "";
285             }
286
287             var String wildcardParam = "";
288             if (t.equals(Types.voidType())) {
289                 builder.append("java.lang.Void" + separator);
290             } else {
291
292                 if (t instanceof WildcardType) {
293                     wildcardParam = "? extends ";
294                 }
295
296                 builder.append(wildcardParam + getExplicitType(t) + separator);
297                 i = i + 1
298             }
299         }
300         return builder.toString();
301     }
302
303     private def generateSubInfo(Module module) '''
304         «FOR submodule : module.submodules»
305             private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
306
307                 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
308
309                 private final «String.importedName» name = "«submodule.name»";
310                 private final «String.importedName» namespace = "«submodule.namespace.toString»";
311                 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
312                 private final «String.importedName» revision = "«df.format(submodule.revision)»";
313                 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
314
315                 private final «Set.importedName»<YangModuleInfo> importedModules;
316
317                 public static «YangModuleInfo.importedName» getInstance() {
318                     return INSTANCE;
319                 }
320
321                 «classBody(submodule, getClassName(submodule.name + "Info"))»
322             }
323         «ENDFOR»
324     '''
325
326 }