Merge "Do not return null when no RPC is registered"
[yangtools.git] / code-generator / binding-java-api-generator / src / main / java / org / opendaylight / yangtools / sal / 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.yangtools.sal.java.api.generator
9
10 import java.io.InputStream
11 import java.io.IOException
12 import java.text.DateFormat
13 import java.text.SimpleDateFormat
14
15 import java.util.Collections
16 import java.util.Date
17 import java.util.HashSet
18 import java.util.LinkedHashMap
19 import java.util.Map
20 import java.util.Set
21 import java.util.TreeMap
22
23 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil
24 import org.opendaylight.yangtools.binding.generator.util.Types
25 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType
26 import org.opendaylight.yangtools.sal.binding.model.api.Type
27 import org.opendaylight.yangtools.sal.binding.model.api.WildcardType
28 import org.opendaylight.yangtools.yang.binding.YangModuleInfo
29 import org.opendaylight.yangtools.yang.model.api.Module
30 import org.opendaylight.yangtools.yang.model.api.SchemaContext
31
32 import com.google.common.collect.ImmutableSet
33
34 class YangModuleInfoTemplate {
35     val CLASS = "$YangModuleInfoImpl"
36
37     val Module module
38     val SchemaContext ctx
39     val Map<String, String> importMap = new LinkedHashMap()
40
41     new (Module module, SchemaContext ctx) {
42         if (module == null) {
43             throw new IllegalArgumentException("Module reference cannot be NULL!")
44         }
45         this.module = module
46         this.ctx = ctx
47     }
48
49     def String generate() {
50         val String classBody = body().toString
51         '''
52         package «BindingGeneratorUtil.moduleNamespaceToPackageName(module)» ;
53
54         «imports»
55
56         «classBody»
57         '''.toString
58     }
59
60     def body() '''
61         public final class «CLASS» implements «YangModuleInfo.importedName» {
62
63             private static final «YangModuleInfo.importedName» INSTANCE = new «CLASS»();
64
65             private final Set<YangModuleInfo> importedModules;
66
67             public static «YangModuleInfo.importedName» getInstance() {
68                 return INSTANCE;
69             }
70
71             «module.classBody»
72         }
73     '''
74
75     private def CharSequence classBody(Module m) '''
76         private «CLASS»() {
77             «IF m.imports.size != 0»
78                 «Set.importedName»<«YangModuleInfo.importedName»> set = new «HashSet.importedName»<>();
79                 «FOR imp : m.imports»
80                     «val name = imp.moduleName»
81                     «val rev = imp.revision»
82                     «IF rev == null»
83                         «val Set<Module> modules = ctx.modules»
84                         «val TreeMap<Date, Module> sorted = new TreeMap()»
85                         «FOR module : modules»
86                             «IF module.name.equals(name)»
87                                 «sorted.put(module.revision, module)»
88                             «ENDIF»
89                         «ENDFOR»
90                         set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(sorted.lastEntry().value)».«CLASS».getInstance());
91                     «ELSE»
92                         set.add(«BindingGeneratorUtil.moduleNamespaceToPackageName(ctx.findModuleByName(name, rev))».«CLASS».getInstance());
93                     «ENDIF»
94                 «ENDFOR»
95                 importedModules = «ImmutableSet.importedName».copyOf(set);
96             «ELSE»
97                 importedModules = «Collections.importedName».emptySet();
98             «ENDIF»
99
100             «val path = m.moduleSourcePath.replace(java.io.File.separatorChar, '/')»
101             «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«path»");
102             if (stream == null) {
103                 throw new IllegalStateException("Resource «path» is missing");
104             }
105             try {
106                 stream.close();
107             } catch («IOException.importedName» e) {
108                 // Resource leak, but there is nothing we can do
109             }
110         }
111
112         @Override
113         public «String.importedName» getName() {
114             return "«m.name»";
115         }
116
117         @Override
118         public «String.importedName» getRevision() {
119             «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
120             return "«df.format(m.revision)»";
121         }
122
123         @Override
124         public «String.importedName» getNamespace() {
125             return "«m.namespace.toString»";
126         }
127
128         @Override
129         public «InputStream.importedName» getModuleSourceStream() throws IOException {
130             «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«path»");
131             if (stream == null) {
132                 throw new «IOException.importedName»("Resource «path» is missing");
133             }
134             return stream;
135         }
136
137         @Override
138         public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
139             return importedModules;
140         }
141     '''
142
143     private def imports() ''' 
144         «IF !importMap.empty»
145             «FOR entry : importMap.entrySet»
146                 «IF entry.value != BindingGeneratorUtil.moduleNamespaceToPackageName(module)»
147                     import «entry.value».«entry.key»;
148                 «ENDIF»
149             «ENDFOR»
150         «ENDIF»
151         
152     '''
153
154     final protected def importedName(Class<?> cls) {
155         val Type intype = Types.typeForClass(cls)
156         putTypeIntoImports(intype);
157         getExplicitType(intype)
158     }
159
160     final def void putTypeIntoImports(Type type) {
161         val String typeName = type.getName();
162         val String typePackageName = type.getPackageName();
163         if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
164             return;
165         }
166         if (!importMap.containsKey(typeName)) {
167             importMap.put(typeName, typePackageName);
168         }
169         if (type instanceof ParameterizedType) {
170             val ParameterizedType paramType = (type as ParameterizedType)
171             val Type[] params = paramType.getActualTypeArguments()
172             if (params != null) {
173                 for (Type param : params) {
174                     putTypeIntoImports(param);
175                 }
176             }
177         }
178     }
179
180     final def String getExplicitType(Type type) {
181         val String typePackageName = type.getPackageName();
182         val String typeName = type.getName();
183         val String importedPackageName = importMap.get(typeName);
184         var StringBuilder builder;
185         if (typePackageName.equals(importedPackageName)) {
186             builder = new StringBuilder(type.getName());
187             addActualTypeParameters(builder, type);
188             if (builder.toString().equals("Void")) {
189                 return "void";
190             }
191         } else {
192             builder = new StringBuilder();
193             if (typePackageName.startsWith("java.lang")) {
194                 builder.append(type.getName());
195             } else {
196                 if (!typePackageName.isEmpty()) {
197                     builder.append(typePackageName + Constants.DOT + type.getName());
198                 } else {
199                     builder.append(type.getName());
200                 }
201             }
202             if (type.equals(Types.voidType())) {
203                 return "void";
204             }
205             addActualTypeParameters(builder, type);
206         }
207         return builder.toString();
208     }
209
210     final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
211         if (type instanceof ParameterizedType) {
212             val ParameterizedType pType = (type as ParameterizedType)
213             val Type[] pTypes = pType.getActualTypeArguments();
214             builder.append("<");
215             builder.append(getParameters(pTypes));
216             builder.append(">");
217         }
218         return builder;
219     }
220
221     final def String getParameters(Type[] pTypes) {
222         if (pTypes == null || pTypes.length == 0) {
223             return "?";
224         }
225         val StringBuilder builder = new StringBuilder();
226         
227         var int i = 0;
228         for (pType : pTypes) {
229             val Type t = pTypes.get(i)
230
231             var String separator = ",";
232             if (i == (pTypes.length - 1)) {
233                 separator = "";
234             }
235
236             var String wildcardParam = "";
237             if (t.equals(Types.voidType())) {
238                 builder.append("java.lang.Void" + separator);
239             } else {
240
241                 if (t instanceof WildcardType) {
242                     wildcardParam = "? extends ";
243                 }
244
245                 builder.append(wildcardParam + getExplicitType(t) + separator);
246                 i = i + 1
247             }
248         }
249         return builder.toString();
250     }
251
252 }