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.yangtools.sal.java.api.generator
10 import static org.opendaylight.yangtools.yang.binding.BindingMapping.getClassName
11 import static org.opendaylight.yangtools.yang.binding.BindingMapping.getRootPackageName
12 import static org.opendaylight.yangtools.yang.binding.BindingMapping.MODEL_BINDING_PROVIDER_CLASS_NAME
13 import static org.opendaylight.yangtools.yang.binding.BindingMapping.MODULE_INFO_CLASS_NAME
15 import com.google.common.base.Preconditions
16 import com.google.common.collect.ImmutableSet
17 import java.io.InputStream
18 import java.io.IOException
19 import java.text.DateFormat
20 import java.text.SimpleDateFormat
21 import java.util.Optional
22 import java.util.Collections
24 import java.util.HashSet
25 import java.util.LinkedHashMap
28 import java.util.TreeMap
29 import java.util.function.Function
30 import org.opendaylight.yangtools.binding.generator.util.Types
31 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
32 import org.opendaylight.yangtools.sal.binding.model.api.Type
33 import org.opendaylight.yangtools.sal.binding.model.api.WildcardType
34 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
35 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
36 import org.opendaylight.yangtools.yang.model.api.Module
37 import org.opendaylight.yangtools.yang.model.api.SchemaContext
39 class YangModuleInfoTemplate {
43 val Map<String, String> importMap = new LinkedHashMap()
44 val Function<Module, Optional<String>> moduleFilePathResolver
47 val String packageName;
50 val String modelBindingProviderName;
52 new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
53 Preconditions.checkArgument(module !== null, "Module must not be null.");
56 this.moduleFilePathResolver = moduleFilePathResolver
57 _packageName = getRootPackageName(module.getQNameModule());
58 _modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»''';
61 def String generate() {
63 public final class «MODULE_INFO_CLASS_NAME» implements «YangModuleInfo.importedName» {
65 private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
67 private final «String.importedName» name = "«module.name»";
68 private final «String.importedName» namespace = "«module.namespace.toString»";
69 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
70 private final «String.importedName» revision = "«df.format(module.revision)»";
71 private final «String.importedName» resourcePath = "«sourcePath(module)»";
73 private final «Set.importedName»<YangModuleInfo> importedModules;
75 public static «YangModuleInfo.importedName» getInstance() {
79 «classBody(module, MODULE_INFO_CLASS_NAME)»
83 package «packageName» ;
89 def String generateModelProvider() {
91 package «packageName»;
93 public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
95 public «YangModuleInfo.name» getModuleInfo() {
96 return «MODULE_INFO_CLASS_NAME».getInstance();
103 private def CharSequence classBody(Module m, String className) '''
104 private «className»() {
105 «IF !m.imports.empty || !m.submodules.empty»
106 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
108 «IF !m.imports.empty»
109 «FOR imp : m.imports»
110 «val name = imp.moduleName»
111 «val rev = imp.revision»
113 «val Set<Module> modules = ctx.modules»
114 «val TreeMap<Date, Module> sorted = new TreeMap()»
115 «FOR module : modules»
116 «IF module.name.equals(name)»
117 «sorted.put(module.revision, module)»
120 set.add(«getRootPackageName(sorted.lastEntry().value.QNameModule)».«MODULE_INFO_CLASS_NAME».getInstance());
122 set.add(«getRootPackageName((ctx.findModuleByName(name, rev).QNameModule))».«MODULE_INFO_CLASS_NAME».getInstance());
126 «IF !m.submodules.empty»
127 «FOR submodule : m.submodules»
128 set.add(«getClassName(submodule.name)»Info.getInstance());
131 «IF m.imports.empty && m.submodules.empty»
132 importedModules = «Collections.importedName».emptySet();
134 importedModules = «ImmutableSet.importedName».copyOf(set);
137 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
138 if (stream == null) {
139 throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
143 } catch («IOException.importedName» e) {
144 // Resource leak, but there is nothing we can do
149 public «String.importedName» getName() {
154 public «String.importedName» getRevision() {
159 public «String.importedName» getNamespace() {
164 public «InputStream.importedName» getModuleSourceStream() throws IOException {
165 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
166 if (stream == null) {
167 throw new «IOException.importedName»("Resource " + resourcePath + " is missing");
173 public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
174 return importedModules;
178 public «String.importedName» toString() {
179 «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
181 sb.append("name = " + name);
182 sb.append(", namespace = " + namespace);
183 sb.append(", revision = " + revision);
184 sb.append(", resourcePath = " + resourcePath);
185 sb.append(", imports = " + importedModules);
187 return sb.toString();
194 private def sourcePath(Module module) {
195 val opt = moduleFilePathResolver.apply(module)
196 Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
200 private def imports() '''
201 «IF !importMap.empty»
202 «FOR entry : importMap.entrySet»
203 «IF entry.value != getRootPackageName(module.QNameModule)»
204 import «entry.value».«entry.key»;
210 final protected def importedName(Class<?> cls) {
211 val Type intype = Types.typeForClass(cls)
212 putTypeIntoImports(intype);
213 getExplicitType(intype)
216 final def void putTypeIntoImports(Type type) {
217 val String typeName = type.getName();
218 val String typePackageName = type.getPackageName();
219 if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
222 if (!importMap.containsKey(typeName)) {
223 importMap.put(typeName, typePackageName);
225 if (type instanceof ParameterizedType) {
226 val Type[] params = type.getActualTypeArguments()
227 if (params !== null) {
228 for (Type param : params) {
229 putTypeIntoImports(param);
235 final def String getExplicitType(Type type) {
236 val String typePackageName = type.getPackageName();
237 val String typeName = type.getName();
238 val String importedPackageName = importMap.get(typeName);
239 var StringBuilder builder;
240 if (typePackageName.equals(importedPackageName)) {
241 builder = new StringBuilder(type.getName());
242 if (builder.toString().equals("Void")) {
245 addActualTypeParameters(builder, type);
247 if (type.equals(Types.voidType())) {
250 builder = new StringBuilder();
251 if (!typePackageName.isEmpty()) {
252 builder.append(typePackageName + Constants.DOT + type.getName());
254 builder.append(type.getName());
256 addActualTypeParameters(builder, type);
258 return builder.toString();
261 final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
262 if (type instanceof ParameterizedType) {
263 val Type[] pTypes = type.getActualTypeArguments();
265 builder.append(getParameters(pTypes));
271 final def String getParameters(Type[] pTypes) {
272 if (pTypes === null || pTypes.length == 0) {
275 val StringBuilder builder = new StringBuilder();
278 for (pType : pTypes) {
279 val Type t = pTypes.get(i)
281 var String separator = ",";
282 if (i == (pTypes.length - 1)) {
286 var String wildcardParam = "";
287 if (t.equals(Types.voidType())) {
288 builder.append("java.lang.Void" + separator);
291 if (t instanceof WildcardType) {
292 wildcardParam = "? extends ";
295 builder.append(wildcardParam + getExplicitType(t) + separator);
299 return builder.toString();
302 private def generateSubInfo(Module module) '''
303 «FOR submodule : module.submodules»
304 private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
306 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
308 private final «String.importedName» name = "«submodule.name»";
309 private final «String.importedName» namespace = "«submodule.namespace.toString»";
310 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
311 private final «String.importedName» revision = "«df.format(submodule.revision)»";
312 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
314 private final «Set.importedName»<YangModuleInfo> importedModules;
316 public static «YangModuleInfo.importedName» getInstance() {
320 «classBody(submodule, getClassName(submodule.name + "Info"))»