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