Cleanup YangModuleInfoTemplate
[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         package «packageName»;
102
103         public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
104             @«Override.importedName»
105             public «YangModuleInfo.name» getModuleInfo() {
106                 return «MODULE_INFO_CLASS_NAME».getInstance();
107             }
108         }
109     '''
110
111     private static def void collectSubmodules(Set<Module> dest, Module module) {
112         for (Module submodule : module.submodules) {
113             if (dest.add(submodule)) {
114                 collectSubmodules(dest, submodule)
115             }
116         }
117     }
118
119     private def CharSequence classBody(Module m, String className, Set<Module> submodules) '''
120         private «className»() {
121             «IF !m.imports.empty || !submodules.empty»
122                 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
123             «ENDIF»
124             «IF !m.imports.empty»
125                 «FOR imp : m.imports»
126                     «val name = imp.moduleName»
127                     «val rev = imp.revision»
128                     «IF !rev.present»
129                         «val Set<Module> modules = ctx.modules»
130                         «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap(REVISION_COMPARATOR)»
131                         «FOR module : modules»
132                             «IF module.name.equals(name)»
133                                 «sorted.put(module.revision, module)»
134                             «ENDIF»
135                         «ENDFOR»
136                         set.add(«sorted.lastEntry().value.QNameModule.rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
137                     «ELSE»
138                         set.add(«(ctx.findModule(name, rev).get.QNameModule).rootPackageName».«MODULE_INFO_CLASS_NAME».getInstance());
139                     «ENDIF»
140                 «ENDFOR»
141             «ENDIF»
142             «FOR submodule : submodules»
143                 set.add(«submodule.name.className»Info.getInstance());
144             «ENDFOR»
145             «IF m.imports.empty && submodules.empty»
146                 importedModules = «ImmutableSet.importedName».of();
147             «ELSE»
148                 importedModules = «ImmutableSet.importedName».copyOf(set);
149             «ENDIF»
150         }
151
152         @«Override.importedName»
153         public «QName.importedName» getName() {
154             return NAME;
155         }
156
157         @«Override.importedName»
158         protected «String.importedName» resourceName() {
159             return "«sourcePath(m)»";
160         }
161
162         @«Override.importedName»
163         public «ImmutableSet.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
164             return importedModules;
165         }
166         «generateSubInfo(submodules)»
167     '''
168
169     private def sourcePath(Module module) {
170         val opt = moduleFilePathResolver.apply(module)
171         Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
172         return opt.get
173     }
174
175     private def imports() '''
176         «IF !importMap.empty»
177             «FOR entry : importMap.entrySet»
178                 «IF entry.value != module.QNameModule.rootPackageName»
179                     import «entry.value».«entry.key»;
180                 «ENDIF»
181             «ENDFOR»
182         «ENDIF»
183     '''
184
185     final protected def importedName(Class<?> cls) {
186         val Type intype = Types.typeForClass(cls)
187         putTypeIntoImports(intype)
188         intype.explicitType
189     }
190
191     final def void putTypeIntoImports(Type type) {
192         val String typeName = type.name
193         val String typePackageName = type.packageName
194         if (typePackageName.startsWith("java.lang") || typePackageName.empty) {
195             return
196         }
197         importMap.putIfAbsent(typeName, typePackageName)
198         if (type instanceof ParameterizedType) {
199             val Type[] params = type.actualTypeArguments
200             if (params !== null) {
201                 for (Type param : params) {
202                     putTypeIntoImports(param)
203                 }
204             }
205         }
206     }
207
208     final def String getExplicitType(Type type) {
209         val String typePackageName = type.packageName
210         val String typeName = type.name
211         val String importedPackageName = importMap.get(typeName)
212         var StringBuilder builder
213         if (typePackageName.equals(importedPackageName)) {
214             builder = new StringBuilder(typeName)
215             if (builder.toString().equals("Void")) {
216                 return "void"
217             }
218             addActualTypeParameters(builder, type)
219         } else {
220             if (type.equals(Types.voidType())) {
221                 return "void"
222             }
223             builder = new StringBuilder()
224             if (!typePackageName.empty) {
225                 builder.append(typePackageName).append(Constants.DOT).append(typeName)
226             } else {
227                 builder.append(typeName)
228             }
229             addActualTypeParameters(builder, type)
230         }
231         return builder.toString()
232     }
233
234     final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
235         if (type instanceof ParameterizedType) {
236             val Type[] pTypes = type.actualTypeArguments
237             builder.append('<').append(getParameters(pTypes)).append('>')
238         }
239         return builder
240     }
241
242     final def String getParameters(Type[] pTypes) {
243         if (pTypes === null || pTypes.length == 0) {
244             return "?"
245         }
246         val StringBuilder builder = new StringBuilder()
247
248         var int i = 0
249         for (pType : pTypes) {
250             val Type t = pTypes.get(i)
251
252             var String separator = ","
253             if (i == (pTypes.length - 1)) {
254                 separator = ""
255             }
256
257             var String wildcardParam = ""
258             if (t.equals(Types.voidType())) {
259                 builder.append("java.lang.Void").append(separator)
260             } else {
261
262                 if (t instanceof WildcardType) {
263                     wildcardParam = "? extends "
264                 }
265
266                 builder.append(wildcardParam).append(t.explicitType).append(separator)
267                 i = i + 1
268             }
269         }
270         return builder.toString()
271     }
272
273     private def generateSubInfo(Set<Module> submodules) '''
274         «FOR submodule : submodules»
275             «val className = submodule.name.className»
276
277             private static final class «className»Info extends «ResourceYangModuleInfo.importedName» {
278                 «val rev = submodule.revision»
279                 private final «QName.importedName» NAME = «QName.importedName».create("«
280                     submodule.namespace.toString»", «IF rev.present»"«rev.get.toString»", «ENDIF»"«submodule.name»").intern();
281                 private static final «YangModuleInfo.importedName» INSTANCE = new «className»Info();
282
283                 private final «ImmutableSet.importedName»<YangModuleInfo> importedModules;
284
285                 public static «YangModuleInfo.importedName» getInstance() {
286                     return INSTANCE;
287                 }
288
289                 «classBody(submodule, className + "Info", ImmutableSet.of)»
290             }
291         «ENDFOR»
292     '''
293 }