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