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.text.DateFormat
20 import java.text.SimpleDateFormat
21 import java.util.Collections
23 import java.util.HashSet
24 import java.util.LinkedHashMap
26 import java.util.Optional
28 import java.util.TreeMap
29 import java.util.function.Function
30 import org.eclipse.xtend.lib.Property
31 import org.opendaylight.mdsal.binding.model.api.ParameterizedType
32 import org.opendaylight.mdsal.binding.model.api.Type
33 import org.opendaylight.mdsal.binding.model.api.WildcardType
34 import org.opendaylight.mdsal.binding.model.util.Types
35 import org.opendaylight.yangtools.yang.binding.YangModelBindingProvider
36 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
37 import org.opendaylight.yangtools.yang.model.api.Module
38 import org.opendaylight.yangtools.yang.model.api.SchemaContext
40 class YangModuleInfoTemplate {
44 val Map<String, String> importMap = new LinkedHashMap()
45 val Function<Module, Optional<String>> moduleFilePathResolver
48 val String packageName;
51 val String modelBindingProviderName;
53 new(Module module, SchemaContext ctx, Function<Module, Optional<String>> moduleFilePathResolver) {
54 Preconditions.checkArgument(module !== null, "Module must not be null.");
57 this.moduleFilePathResolver = moduleFilePathResolver
58 _packageName = getRootPackageName(module.getQNameModule());
59 _modelBindingProviderName = '''«packageName».«MODEL_BINDING_PROVIDER_CLASS_NAME»''';
62 def String generate() {
64 public final class «MODULE_INFO_CLASS_NAME» implements «YangModuleInfo.importedName» {
66 private static final «YangModuleInfo.importedName» INSTANCE = new «MODULE_INFO_CLASS_NAME»();
68 private final «String.importedName» name = "«module.name»";
69 private final «String.importedName» namespace = "«module.namespace.toString»";
70 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
71 private final «String.importedName» revision = "«df.format(module.revision)»";
72 private final «String.importedName» resourcePath = "«sourcePath(module)»";
74 private final «Set.importedName»<YangModuleInfo> importedModules;
76 public static «YangModuleInfo.importedName» getInstance() {
80 «classBody(module, MODULE_INFO_CLASS_NAME)»
84 package «packageName» ;
90 def String generateModelProvider() {
92 package «packageName»;
94 public final class «MODEL_BINDING_PROVIDER_CLASS_NAME» implements «YangModelBindingProvider.name» {
96 public «YangModuleInfo.name» getModuleInfo() {
97 return «MODULE_INFO_CLASS_NAME».getInstance();
104 private def CharSequence classBody(Module m, String className) '''
105 private «className»() {
106 «IF !m.imports.empty || !m.submodules.empty»
107 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
109 «IF !m.imports.empty»
110 «FOR imp : m.imports»
111 «val name = imp.moduleName»
112 «val rev = imp.revision»
114 «val Set<Module> modules = ctx.modules»
115 «val TreeMap<Date, Module> sorted = new TreeMap()»
116 «FOR module : modules»
117 «IF module.name.equals(name)»
118 «sorted.put(module.revision, module)»
121 set.add(«getRootPackageName(sorted.lastEntry().value.QNameModule)».«MODULE_INFO_CLASS_NAME».getInstance());
123 set.add(«getRootPackageName((ctx.findModuleByName(name, rev).QNameModule))».«MODULE_INFO_CLASS_NAME».getInstance());
127 «IF !m.submodules.empty»
128 «FOR submodule : m.submodules»
129 set.add(«getClassName(submodule.name)»Info.getInstance());
132 «IF m.imports.empty && m.submodules.empty»
133 importedModules = «Collections.importedName».emptySet();
135 importedModules = «ImmutableSet.importedName».copyOf(set);
138 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
139 if (stream == null) {
140 throw new IllegalStateException("Resource '" + resourcePath + "' is missing");
144 } catch («IOException.importedName» e) {
145 // Resource leak, but there is nothing we can do
150 public «String.importedName» getName() {
155 public «String.importedName» getRevision() {
160 public «String.importedName» getNamespace() {
165 public «InputStream.importedName» getModuleSourceStream() throws IOException {
166 «InputStream.importedName» stream = «MODULE_INFO_CLASS_NAME».class.getResourceAsStream(resourcePath);
167 if (stream == null) {
168 throw new «IOException.importedName»("Resource " + resourcePath + " is missing");
174 public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
175 return importedModules;
179 public «String.importedName» toString() {
180 «StringBuilder.importedName» sb = new «StringBuilder.importedName»(this.getClass().getCanonicalName());
182 sb.append("name = " + name);
183 sb.append(", namespace = " + namespace);
184 sb.append(", revision = " + revision);
185 sb.append(", resourcePath = " + resourcePath);
186 sb.append(", imports = " + importedModules);
188 return sb.toString();
195 private def sourcePath(Module module) {
196 val opt = moduleFilePathResolver.apply(module)
197 Preconditions.checkState(opt.isPresent, "Module %s does not have a file path", module)
201 private def imports() '''
202 «IF !importMap.empty»
203 «FOR entry : importMap.entrySet»
204 «IF entry.value != getRootPackageName(module.QNameModule)»
205 import «entry.value».«entry.key»;
211 final protected def importedName(Class<?> cls) {
212 val Type intype = Types.typeForClass(cls)
213 putTypeIntoImports(intype);
214 getExplicitType(intype)
217 final def void putTypeIntoImports(Type type) {
218 val String typeName = type.getName();
219 val String typePackageName = type.getPackageName();
220 if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
223 if (!importMap.containsKey(typeName)) {
224 importMap.put(typeName, typePackageName);
226 if (type instanceof ParameterizedType) {
227 val Type[] params = type.getActualTypeArguments()
228 if (params !== null) {
229 for (Type param : params) {
230 putTypeIntoImports(param);
236 final def String getExplicitType(Type type) {
237 val String typePackageName = type.getPackageName();
238 val String typeName = type.getName();
239 val String importedPackageName = importMap.get(typeName);
240 var StringBuilder builder;
241 if (typePackageName.equals(importedPackageName)) {
242 builder = new StringBuilder(type.getName());
243 if (builder.toString().equals("Void")) {
246 addActualTypeParameters(builder, type);
248 if (type.equals(Types.voidType())) {
251 builder = new StringBuilder();
252 if (!typePackageName.isEmpty()) {
253 builder.append(typePackageName + Constants.DOT + type.getName());
255 builder.append(type.getName());
257 addActualTypeParameters(builder, type);
259 return builder.toString();
262 final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
263 if (type instanceof ParameterizedType) {
264 val Type[] pTypes = type.getActualTypeArguments();
266 builder.append(getParameters(pTypes));
272 final def String getParameters(Type[] pTypes) {
273 if (pTypes === null || pTypes.length == 0) {
276 val StringBuilder builder = new StringBuilder();
279 for (pType : pTypes) {
280 val Type t = pTypes.get(i)
282 var String separator = ",";
283 if (i == (pTypes.length - 1)) {
287 var String wildcardParam = "";
288 if (t.equals(Types.voidType())) {
289 builder.append("java.lang.Void" + separator);
292 if (t instanceof WildcardType) {
293 wildcardParam = "? extends ";
296 builder.append(wildcardParam + getExplicitType(t) + separator);
300 return builder.toString();
303 private def generateSubInfo(Module module) '''
304 «FOR submodule : module.submodules»
305 private static final class «getClassName(submodule.name)»Info implements «YangModuleInfo.importedName» {
307 private static final «YangModuleInfo.importedName» INSTANCE = new «getClassName(submodule.name)»Info();
309 private final «String.importedName» name = "«submodule.name»";
310 private final «String.importedName» namespace = "«submodule.namespace.toString»";
311 «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
312 private final «String.importedName» revision = "«df.format(submodule.revision)»";
313 private final «String.importedName» resourcePath = "«sourcePath(submodule)»";
315 private final «Set.importedName»<YangModuleInfo> importedModules;
317 public static «YangModuleInfo.importedName» getInstance() {
321 «classBody(submodule, getClassName(submodule.name + "Info"))»