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