2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.java.api.generator
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.getClassName
13 import static org.opendaylight.yangtools.yang.binding.BindingMapping.getRootPackageName
15 import com.google.common.base.Preconditions
16 import com.google.common.collect.ImmutableSet
17 import java.io.IOException
18 import java.io.InputStream
19 import java.util.Collections
20 import java.util.HashSet
21 import java.util.LinkedHashMap
23 import java.util.Optional
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.YangModelBindingProvider
33 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
34 import org.opendaylight.yangtools.yang.model.api.Module
35 import org.opendaylight.yangtools.yang.model.api.SchemaContext
36 import org.opendaylight.yangtools.yang.common.Revision
38 class YangModuleInfoTemplate {
42 val Map<String, String> importMap = new LinkedHashMap()
43 val Function<Module, Optional<String>> moduleFilePathResolver
46 val String packageName;
49 val String modelBindingProviderName;
51 new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
52 Preconditions.checkArgument(module !== null, "Module must not be null.");
55 this.moduleFilePathResolver = moduleFilePathResolver
56 packageName = getRootPackageName(module.getQNameModule());
57 modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»''';
60 def String generate() {
62 public final class «MODULE_INFO_CLASS_NAME» implements «YangModuleInfo.importedName» {
64 private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
66 private final «String.importedName» name = "«module.name»";
67 private final «String.importedName» namespace = "«module.namespace.toString»";
68 «IF module.revision.isPresent»
69 private final «String.importedName» revision = "«module.revision.get.toString»";
71 private final «String.importedName» revision = null;
73 private final «String.importedName» resourcePath = "«sourcePath(module)»";
75 private final «Set.importedName»<YangModuleInfo> importedModules;
77 public static «YangModuleInfo.importedName» getInstance() {
81 «classBody(module, MODULE_INFO_CLASS_NAME)»
85 package «packageName» ;
91 def String generateModelProvider() {
93 package «packageName»;
95 public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
97 public «YangModuleInfo.name» getModuleInfo() {
98 return «MODULE_INFO_CLASS_NAME».getInstance();
105 private def CharSequence classBody(Module m, String className) '''
106 private «className»() {
107 «IF !m.imports.empty || !m.submodules.empty»
108 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
110 «IF !m.imports.empty»
111 «FOR imp : m.imports»
112 «val name = imp.moduleName»
113 «val rev = imp.revision»
115 «val Set<Module> modules = ctx.modules»
116 «val TreeMap<Optional<Revision>, Module> sorted = new TreeMap()»
117 «FOR module : modules»
118 «IF module.name.equals(name)»
119 «sorted.put(module.revision, module)»
122 set.add(«getRootPackageName(sorted.lastEntry().value.QNameModule)».«MODULE_INFO_CLASS_NAME».getInstance());
124 set.add(«getRootPackageName((ctx.findModule(name, rev).get.QNameModule))».«MODULE_INFO_CLASS_NAME».getInstance());
128 «IF !m.submodules.empty»
129 «FOR submodule : m.submodules»
130 set.add(«getClassName(submodule.name)»Info.getInstance());
133 «IF m.imports.empty && m.submodules.empty»
134 importedModules = «Collections.importedName».emptySet();
136 importedModules = «ImmutableSet.importedName».copyOf(set);
139 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
140 if (stream == null) {
141 throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
145 } catch («IOException.importedName» e) {
146 // Resource leak, but there is nothing we can do
151 public «String.importedName» getName() {
156 public «String.importedName» getRevision() {
161 public «String.importedName» getNamespace() {
166 public «InputStream.importedName» getModuleSourceStream() throws IOException {
167 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
168 if (stream == null) {
169 throw new «IOException.importedName»("Resource " + resourcePath + " is missing");
175 public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
176 return importedModules;
180 public «String.importedName» toString() {
181 «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
183 sb.append("name = " + name);
184 sb.append(", namespace = " + namespace);
185 sb.append(", revision = " + revision);
186 sb.append(", resourcePath = " + resourcePath);
187 sb.append(", imports = " + importedModules);
189 return sb.toString();
196 private def sourcePath(Module module) {
197 val opt = moduleFilePathResolver.apply(module)
198 Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
202 private def imports() '''
203 «IF !importMap.empty»
204 «FOR entry : importMap.entrySet»
205 «IF entry.value != getRootPackageName(module.QNameModule)»
206 import «entry.value».«entry.key»;
212 final protected def importedName(Class<?> cls) {
213 val Type intype = Types.typeForClass(cls)
214 putTypeIntoImports(intype);
215 getExplicitType(intype)
218 final def void putTypeIntoImports(Type type) {
219 val String typeName = type.getName();
220 val String typePackageName = type.getPackageName();
221 if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
224 if (!importMap.containsKey(typeName)) {
225 importMap.put(typeName, typePackageName);
227 if (type instanceof ParameterizedType) {
228 val Type[] params = type.getActualTypeArguments()
229 if (params !== null) {
230 for (Type param : params) {
231 putTypeIntoImports(param);
237 final def String getExplicitType(Type type) {
238 val String typePackageName = type.getPackageName();
239 val String typeName = type.getName();
240 val String importedPackageName = importMap.get(typeName);
241 var StringBuilder builder;
242 if (typePackageName.equals(importedPackageName)) {
243 builder = new StringBuilder(type.getName());
244 if (builder.toString().equals("Void")) {
247 addActualTypeParameters(builder, type);
249 if (type.equals(Types.voidType())) {
252 builder = new StringBuilder();
253 if (!typePackageName.isEmpty()) {
254 builder.append(typePackageName + Constants.DOT + type.getName());
256 builder.append(type.getName());
258 addActualTypeParameters(builder, type);
260 return builder.toString();
263 final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
264 if (type instanceof ParameterizedType) {
265 val Type[] pTypes = type.getActualTypeArguments();
267 builder.append(getParameters(pTypes));
273 final def String getParameters(Type[] pTypes) {
274 if (pTypes === null || pTypes.length == 0) {
277 val StringBuilder builder = new StringBuilder();
280 for (pType : pTypes) {
281 val Type t = pTypes.get(i)
283 var String separator = ",";
284 if (i == (pTypes.length - 1)) {
288 var String wildcardParam = "";
289 if (t.equals(Types.voidType())) {
290 builder.append("java.lang.Void" + separator);
293 if (t instanceof WildcardType) {
294 wildcardParam = "? extends ";
297 builder.append(wildcardParam + getExplicitType(t) + separator);
301 return builder.toString();
304 private def generateSubInfo(Module module) '''
305 «FOR submodule : module.submodules»
306 private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
308 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
310 private final «String.importedName» name = "«submodule.name»";
311 private final «String.importedName» namespace = "«submodule.namespace.toString»";
312 «IF submodule.revision.isPresent»
313 private final «String.importedName» revision = "«submodule.revision.get.toString»";
315 private final «String.importedName» revision = null;
317 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
319 private final «Set.importedName»<YangModuleInfo> importedModules;
321 public static «YangModuleInfo.importedName» getInstance() {
325 «classBody(submodule, getClassName(submodule.name + "Info"))»