Update YangModuleInfo and its implementations
[mdsal.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»
101             «IF path != null»
102                 «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«path»");
103                 if (stream == null) {
104                     throw new IllegalStateException("Resource «path» is missing");
105                 }
106                 try {
107                     stream.close();
108                 } catch («IOException.importedName» e) {
109                     // Resource leak, but there's nothing we can do
110                 }
111             «ENDIF»
112         }
113
114         @Override
115         public «String.importedName» getName() {
116             return "«m.name»";
117         }
118
119         @Override
120         public «String.importedName» getRevision() {
121             «val DateFormat df = new SimpleDateFormat("yyyy-MM-dd")»
122             return "«df.format(m.revision)»";
123         }
124
125         @Override
126         public «String.importedName» getNamespace() {
127             return "«m.namespace.toString»";
128         }
129
130         @Override
131         public «InputStream.importedName» getModuleSourceStream() throws IOException {
132             «IF path == null»
133                 return null;
134             «ELSE»
135                 «InputStream.importedName» stream = «CLASS».class.getResourceAsStream("«path»");
136                 if (stream == null) {
137                     throw new «IOException.importedName»("Resource «path» is missing");
138                 }
139                 return stream;
140             «ENDIF»
141         }
142
143         @Override
144         public «Set.importedName»<«YangModuleInfo.importedName»> getImportedModules() {
145             return importedModules;
146         }
147     '''
148
149     private def imports() ''' 
150         «IF !importMap.empty»
151             «FOR entry : importMap.entrySet»
152                 «IF entry.value != BindingGeneratorUtil.moduleNamespaceToPackageName(module)»
153                     import «entry.value».«entry.key»;
154                 «ENDIF»
155             «ENDFOR»
156         «ENDIF»
157         
158     '''
159
160     final protected def importedName(Class<?> cls) {
161         val Type intype = Types.typeForClass(cls)
162         putTypeIntoImports(intype);
163         getExplicitType(intype)
164     }
165
166     final def void putTypeIntoImports(Type type) {
167         val String typeName = type.getName();
168         val String typePackageName = type.getPackageName();
169         if (typePackageName.startsWith("java.lang") || typePackageName.isEmpty()) {
170             return;
171         }
172         if (!importMap.containsKey(typeName)) {
173             importMap.put(typeName, typePackageName);
174         }
175         if (type instanceof ParameterizedType) {
176             val ParameterizedType paramType = (type as ParameterizedType)
177             val Type[] params = paramType.getActualTypeArguments()
178             if (params != null) {
179                 for (Type param : params) {
180                     putTypeIntoImports(param);
181                 }
182             }
183         }
184     }
185
186     final def String getExplicitType(Type type) {
187         val String typePackageName = type.getPackageName();
188         val String typeName = type.getName();
189         val String importedPackageName = importMap.get(typeName);
190         var StringBuilder builder;
191         if (typePackageName.equals(importedPackageName)) {
192             builder = new StringBuilder(type.getName());
193             addActualTypeParameters(builder, type);
194             if (builder.toString().equals("Void")) {
195                 return "void";
196             }
197         } else {
198             builder = new StringBuilder();
199             if (typePackageName.startsWith("java.lang")) {
200                 builder.append(type.getName());
201             } else {
202                 if (!typePackageName.isEmpty()) {
203                     builder.append(typePackageName + Constants.DOT + type.getName());
204                 } else {
205                     builder.append(type.getName());
206                 }
207             }
208             if (type.equals(Types.voidType())) {
209                 return "void";
210             }
211             addActualTypeParameters(builder, type);
212         }
213         return builder.toString();
214     }
215
216     final def StringBuilder addActualTypeParameters(StringBuilder builder, Type type) {
217         if (type instanceof ParameterizedType) {
218             val ParameterizedType pType = (type as ParameterizedType)
219             val Type[] pTypes = pType.getActualTypeArguments();
220             builder.append("<");
221             builder.append(getParameters(pTypes));
222             builder.append(">");
223         }
224         return builder;
225     }
226
227     final def String getParameters(Type[] pTypes) {
228         if (pTypes == null || pTypes.length == 0) {
229             return "?";
230         }
231         val StringBuilder builder = new StringBuilder();
232         
233         var int i = 0;
234         for (pType : pTypes) {
235             val Type t = pTypes.get(i)
236
237             var String separator = ",";
238             if (i == (pTypes.length - 1)) {
239                 separator = "";
240             }
241
242             var String wildcardParam = "";
243             if (t.equals(Types.voidType())) {
244                 builder.append("java.lang.Void" + separator);
245             } else {
246
247                 if (t instanceof WildcardType) {
248                     wildcardParam = "? extends ";
249                 }
250
251                 builder.append(wildcardParam + getExplicitType(t) + separator);
252                 i = i + 1
253             }
254         }
255         return builder.toString();
256     }
257
258 }