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