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 int hashCode() {
181 return «className».class.hashCode();
185 public boolean equals(Object obj) {
186 return obj != null && «className».class.equals(obj.getClass());
190 public «String.importedName» toString() {
191 «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
193 sb.append("name = " + name);
194 sb.append(", namespace = " + namespace);
195 sb.append(", revision = " + revision);
196 sb.append(", resourcePath = " + resourcePath);
197 sb.append(", imports = " + importedModules);
199 return sb.toString();
206 private def sourcePath(Module module) {
207 val opt = moduleFilePathResolver.apply(module)
208 Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
212 private def imports() '''
213 «IF !importMap.empty»
214 «FOR entry : importMap.entrySet»
215 «IF entry.value != getRootPackageName(module.QNameModule)»
216 import «entry.value».«entry.key»;
222 final protected def importedName(Class<?> cls) {
223 val Type intype = Types.typeForClass(cls)
224 putTypeIntoImports(intype);
225 getExplicitType(intype)
228 final def void putTypeIntoImports(Type type) {
229 val String typeName = type.getName();
230 val String typePackageName = type.getPackageName();
231 if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
234 if (!importMap.containsKey(typeName)) {
235 importMap.put(typeName, typePackageName);
237 if (type instanceof ParameterizedType) {
238 val Type[] params = type.getActualTypeArguments()
239 if (params !== null) {
240 for (Type param : params) {
241 putTypeIntoImports(param);
247 final def String getExplicitType(Type type) {
248 val String typePackageName = type.getPackageName();
249 val String typeName = type.getName();
250 val String importedPackageName = importMap.get(typeName);
251 var StringBuilder builder;
252 if (typePackageName.equals(importedPackageName)) {
253 builder = new StringBuilder(type.getName());
254 if (builder.toString().equals("Void")) {
257 addActualTypeParameters(builder, type);
259 if (type.equals(Types.voidType())) {
262 builder = new StringBuilder();
263 if (!typePackageName.isEmpty()) {
264 builder.append(typePackageName + Constants.DOT + type.getName());
266 builder.append(type.getName());
268 addActualTypeParameters(builder, type);
270 return builder.toString();
273 final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
274 if (type instanceof ParameterizedType) {
275 val Type[] pTypes = type.getActualTypeArguments();
277 builder.append(getParameters(pTypes));
283 final def String getParameters(Type[] pTypes) {
284 if (pTypes === null || pTypes.length == 0) {
287 val StringBuilder builder = new StringBuilder();
290 for (pType : pTypes) {
291 val Type t = pTypes.get(i)
293 var String separator = ",";
294 if (i == (pTypes.length - 1)) {
298 var String wildcardParam = "";
299 if (t.equals(Types.voidType())) {
300 builder.append("java.lang.Void" + separator);
303 if (t instanceof WildcardType) {
304 wildcardParam = "? extends ";
307 builder.append(wildcardParam + getExplicitType(t) + separator);
311 return builder.toString();
314 private def generateSubInfo(Module module) '''
315 «FOR submodule : module.submodules»
316 private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
318 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
320 private final «String.importedName» name = "«submodule.name»";
321 private final «String.importedName» namespace = "«submodule.namespace.toString»";
322 «IF submodule.revision.isPresent»
323 private final «String.importedName» revision = "«submodule.revision.get.toString»";
325 private final «String.importedName» revision = null;
327 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
329 private final «Set.importedName»<YangModuleInfo> importedModules;
331 public static «YangModuleInfo.importedName» getInstance() {
335 «classBody(submodule, getClassName(submodule.name + "Info"))»