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