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