Add YangModuleInfoTemplate hashCode/equals
[yangtools.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 int hashCode() {
181             return «className».class.hashCode();
182         }
183
184         @Override
185         public boolean equals(Object obj) {
186             return obj != null && «className».class.equals(obj.getClass());
187         }
188
189         @Override
190         public «String.importedName» toString() {
191             «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
192             sb.append("[");
193             sb.append("name = " + name);
194             sb.append(", namespace = " + namespace);
195             sb.append(", revision = " + revision);
196             sb.append(", resourcePath = " + resourcePath);
197             sb.append(", imports = " + importedModules);
198             sb.append("]");
199             return sb.toString();
200         }
201
202         «generateSubInfo(m)»
203
204     '''
205
206     private def sourcePath(Module module) {
207         val opt = moduleFilePathResolver.apply(module)
208         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
209         return opt.get
210     }
211
212     private def imports() '''
213         «IF !importMap.empty»
214             «FOR entry : importMap.entrySet»
215                 «IF entry.value != getRootPackageName(module.QNameModule)»
216                     import «entry.value».«entry.key»;
217                 «ENDIF»
218             «ENDFOR»
219         «ENDIF»
220     '''
221
222     final protected def importedName(Class<?> cls) {
223         val Type intype = Types.typeForClass(cls)
224         putTypeIntoImports(intype);
225         getExplicitType(intype)
226     }
227
228     final def void putTypeIntoImports(Type type) {
229         val String typeName = type.getName();
230         val String typePackageName = type.getPackageName();
231         if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
232             return;
233         }
234         if (!importMap.containsKey(typeName)) {
235             importMap.put(typeName, typePackageName);
236         }
237         if (type instanceof ParameterizedType) {
238             val Type[] params = type.getActualTypeArguments()
239             if (params !== null) {
240                 for (Type param : params) {
241                     putTypeIntoImports(param);
242                 }
243             }
244         }
245     }
246
247     final def String getExplicitType(Type type) {
248         val String typePackageName = type.getPackageName();
249         val String typeName = type.getName();
250         val String importedPackageName = importMap.get(typeName);
251         var StringBuilder builder;
252         if (typePackageName.equals(importedPackageName)) {
253             builder = new StringBuilder(type.getName());
254             if (builder.toString().equals("Void")) {
255                 return "void";
256             }
257             addActualTypeParameters(builder, type);
258         } else {
259             if (type.equals(Types.voidType())) {
260                 return "void";
261             }
262             builder = new StringBuilder();
263             if (!typePackageName.isEmpty()) {
264                 builder.append(typePackageName + Constants.DOT + type.getName());
265             } else {
266                 builder.append(type.getName());
267             }
268             addActualTypeParameters(builder, type);
269         }
270         return builder.toString();
271     }
272
273     final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
274         if (type instanceof ParameterizedType) {
275             val Type[] pTypes = type.getActualTypeArguments();
276             builder.append('<');
277             builder.append(getParameters(pTypes));
278             builder.append('>');
279         }
280         return builder;
281     }
282
283     final def String getParameters(Type[] pTypes) {
284         if (pTypes === null || pTypes.length == 0) {
285             return "?";
286         }
287         val StringBuilder builder = new StringBuilder();
288
289         var int i = 0;
290         for (pType : pTypes) {
291             val Type t = pTypes.get(i)
292
293             var String separator = ",";
294             if (i == (pTypes.length - 1)) {
295                 separator = "";
296             }
297
298             var String wildcardParam = "";
299             if (t.equals(Types.voidType())) {
300                 builder.append("java.lang.Void" + separator);
301             } else {
302
303                 if (t instanceof WildcardType) {
304                     wildcardParam = "? extends ";
305                 }
306
307                 builder.append(wildcardParam + getExplicitType(t) + separator);
308                 i = i + 1
309             }
310         }
311         return builder.toString();
312     }
313
314     private def generateSubInfo(Module module) '''
315         «FOR submodule : module.submodules»
316             private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
317
318                 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
319
320                 private final «String.importedName» name = "«submodule.name»";
321                 private final «String.importedName» namespace = "«submodule.namespace.toString»";
322                 «IF submodule.revision.isPresent»
323                 private final «String.importedName» revision = "«submodule.revision.get.toString»";
324                 «ELSE»
325                 private final «String.importedName» revision = null;
326                 «ENDIF»
327                 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
328
329                 private final «Set.importedName»<YangModuleInfo> importedModules;
330
331                 public static «YangModuleInfo.importedName» getInstance() {
332                     return INSTANCE;
333                 }
334
335                 «classBody(submodule, getClassName(submodule.name + "Info"))»
336             }
337         «ENDFOR»
338     '''
339 }