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