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