contrib/jwrapper: main logic use `JavaModel` and support many files at once
[nit.git] / contrib / jwrapper / src / model.nit
1 # This file is part of NIT (http://www.nitlanguage.org).
2 #
3 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
4 # Copyright 2015 Alexis Laferrière <alexis.laf@xymus.net>
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 # http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17
18 # Contains the java and nit type representation used to convert java to nit code
19 module model
20
21 import more_collections
22
23 import jtype_converter
24
25 class JavaType
26 private var converter: JavaTypeConverter
27 var identifier = new Array[String]
28 var generic_params: nullable Array[JavaType] = null
29 var is_void = false
30
31 # Has some generic type to be resolved (T extends foo => T is resolved to foo)
32 var has_unresolved_types = false
33
34 # Dimension of primitive array: `int[][]` is 2d
35 var array_dimension = 0
36
37 fun is_primitive_array: Bool do return array_dimension > 0
38
39 fun has_generic_params: Bool do return not generic_params == null
40 fun full_id: String do return identifier.join(".")
41 fun id: String do return identifier.last.replace("$", "")
42
43 init(converter: JavaTypeConverter) do self.converter = converter
44
45 fun return_cast: String do return converter.cast_as_return(self.id)
46
47 fun param_cast: String
48 do
49 if self.has_generic_params then
50 return converter.cast_as_param(self.generic_params[0].id)
51 end
52
53 return converter.cast_as_param(self.id)
54 end
55
56 fun to_nit_type: NitType
57 do
58 var nit_type: NitType
59 var type_id = null
60
61 if not is_primitive_array then
62 type_id = converter.to_nit_type(self.id)
63 end
64
65 if type_id == null then
66 nit_type = self.extern_name
67 nit_type.is_complete = false
68 else
69 nit_type = new NitType(type_id)
70 end
71
72 if not self.has_generic_params then return nit_type
73
74 nit_type.generic_params = new Array[NitType]
75
76 for param in generic_params do
77 var nit_param = param.to_nit_type
78
79 nit_type.generic_params.add(nit_param)
80
81 if not nit_param.is_complete then nit_type.is_complete = false
82 end
83
84 return nit_type
85 end
86
87 fun is_iterable: Bool do return iterable.has(self.id)
88
89 fun is_collection: Bool do return is_primitive_array or collections_list.has(self.id)
90
91 fun is_wrapped: Bool do return find_extern_class != null
92
93 fun extern_name: NitType
94 do
95 if is_wrapped then return new NitType.with_module(find_extern_class.as(not null).first, find_extern_class.as(not null).second)
96
97 var name
98 if is_primitive_array then
99 # Primitive arrays have a special naming convention
100 name = "Native" + extern_class_name.join.capitalized + "Array"
101 else
102 name = "Native" + extern_class_name.join
103 end
104
105 var nit_type = new NitType(name)
106 nit_type.is_complete = false
107 return nit_type
108 end
109
110 fun to_cast(jtype: String, is_param: Bool): String
111 do
112 if is_param then
113 return converter.cast_as_param(jtype)
114 end
115
116 return converter.cast_as_return(jtype)
117 end
118
119 redef fun to_s: String
120 do
121 var id = self.full_id
122
123 if self.is_primitive_array then
124 for i in [0..array_dimension[ do
125 id += "[]"
126 end
127 else if self.has_generic_params then
128 var gen_list = new Array[String]
129
130 for param in generic_params do
131 gen_list.add(param.to_s)
132 end
133
134 id += "<{gen_list.join(", ")}>"
135 end
136
137 return id
138 end
139
140 # To fully qualified package name
141 # Cuts the primitive array `[]`
142 fun to_package_name: String
143 do
144 var str = self.to_s
145 var len = str.length
146
147 return str.substring(0, len - (2*array_dimension))
148 end
149
150 fun resolve_types(conversion_map: HashMap[String, Array[String]])
151 do
152 if identifier.length == 1 then
153 var resolved_id = conversion_map.get_or_null(self.id)
154 if resolved_id != null then self.identifier = new Array[String].from(resolved_id)
155 end
156
157 if self.has_generic_params then
158 for params in generic_params do params.resolve_types(conversion_map)
159 end
160 end
161
162 private fun extern_class_name: Array[String]
163 do
164 var class_name = new Array[String]
165 class_name.add(self.id)
166
167 if not self.has_generic_params then return class_name
168
169 class_name.add "Of"
170
171 for param in generic_params do class_name.add_all param.extern_class_name
172
173 return class_name
174 end
175
176 # Search inside `lib/android` directory for already wrapped classes
177 # If found, contains the class identifier and the Nit Module name
178 var find_extern_class: nullable Couple[String, NitModule] is lazy do
179
180 var regex = "extern class [a-zA-Z1-9]\\\+[ ]\\\+in[ ]\\\+\"Java\"[ ]*`\{[ ]*" + self.to_s + "\\\+[ ]*`\}"
181 var nit_dir = "NIT_DIR".environ
182 var grep = new ProcessReader("grep", "-r", regex, nit_dir/"lib/android/", nit_dir/"lib/java/")
183 var to_eat = ["private", "extern", "class"]
184
185 var output = grep.read_line
186
187 var output_class = output.substring_from(output.index_of(':') + 1)
188 var tokens = output_class.split(" ")
189
190 var nclass_name = ""
191
192 for token in tokens do
193 if to_eat.has(token) then continue
194 nclass_name = token
195 break
196 end
197
198 if nclass_name == "" then return null
199
200 var str = output.substring(0, output.search(".nit").from)
201 str = str.substring_from(str.last_index_of('/') + 1)
202 var mod = new NitModule(str)
203
204 return new Couple[String, NitModule](nclass_name, mod)
205 end
206
207 # Comparison based on fully qualified named and generic params
208 # Ignores primitive array so `a.b.c[][] == a.b.c`
209 redef fun ==(other)
210 do
211 if other isa JavaType then
212 return self.repr == other.repr
213 end
214 return false
215 end
216
217 redef fun hash do return self.repr.hash
218
219 private fun repr: String
220 do
221 var id = self.full_id
222
223 if self.has_generic_params then
224 var gen_list = new Array[String]
225
226 for param in generic_params do
227 gen_list.add(param.to_s)
228 end
229
230 id += "<{gen_list.join(", ")}>"
231 end
232
233 return id
234 end
235
236 var collections_list: Array[String] is lazy do return ["List", "ArrayList", "LinkedList", "Vector", "Set", "SortedSet", "HashSet", "TreeSet", "LinkedHashSet", "Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
237 var iterable: Array[String] is lazy do return ["ArrayList", "Set", "HashSet", "LinkedHashSet", "LinkedList", "Stack", "TreeSet", "Vector"]
238 var maps: Array[String] is lazy do return ["Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
239 end
240
241 class NitType
242 var identifier: String
243 var arg_id: String
244 var generic_params: nullable Array[NitType] = null
245
246 # If this NitType was found in `lib/android`, contains the module name to import
247 var mod: nullable NitModule
248
249 # Returns `true` if all types have been successfully converted to Nit type
250 var is_complete: Bool = true
251
252 fun has_generic_params: Bool do return not generic_params == null
253
254 fun id: String do return identifier
255
256 init (id: String)
257 do
258 self.identifier = id
259 end
260
261 init with_generic_params(id: String, gen_params: String...)
262 do
263 self.init(id)
264 self.generic_params = new Array[NitType]
265 for param in gen_params do self.generic_params.add new NitType(param)
266 end
267
268 init with_module(id: String, mod: NitModule)
269 do
270 self.init(id)
271 self.mod = mod
272 end
273
274 redef fun to_s: String
275 do
276 var id = self.identifier
277
278 if self.has_generic_params then
279 var gen_list = new Array[String]
280
281 for param in generic_params do
282 gen_list.add(param.to_s)
283 end
284
285 id += "[{gen_list.join(", ")}]"
286 end
287
288 return id
289 end
290 end
291
292 # Model of a single Java class
293 class JavaClass
294 # Type of this class
295 var class_type = new JavaType(new JavaTypeConverter)
296
297 # Attributes of this class
298 var attributes = new HashMap[String, JavaType]
299
300 # Methods of this class organized by their name
301 var methods = new MultiHashMap[String, JavaMethod]
302
303 # Importations from this class
304 var imports = new HashSet[NitModule]
305 end
306
307 # Model of all the Java class analyzed in one run
308 class JavaModel
309 # Unknown Java types used in `classes`
310 var unknown_types = new HashSet[JavaType]
311
312 # All analyzed classes
313 var classes = new Array[JavaClass]
314 end
315
316 # A Java method, with its signature
317 class JavaMethod
318 # Type returned by the method
319 var return_type: JavaType
320
321 # Type of the arguments of the method
322 var params: Array[JavaType]
323 end
324
325 # A Nit module, use to import the referenced extern classes
326 class NitModule
327 # Name of the module
328 var name: String
329
330 redef fun ==(other): Bool do return self.to_s == other.to_s
331 redef fun to_s: String do return self.name
332 redef fun hash: Int do return self.name.hash
333 end