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