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