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