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