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