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