contrib/jwrapper: convert style of getters and setters to Nit
[nit.git] / contrib / jwrapper / src / code_generator.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 # Services to generate extern class `in "Java"`
18 module code_generator
19
20 intrude import types
21
22 class CodeGenerator
23
24 var with_attributes: Bool
25 var comment_unknown_types: Bool
26 var file_out: OFStream
27 var java_class: JavaClass
28 var nb_params: Int
29 var module_name: String
30 fun code_warehouse: CodeWarehouse do return once new CodeWarehouse
31
32 init (file_name: String, jclass: JavaClass, with_attributes, comment: Bool)
33 do
34 file_out = new OFStream.open(file_name)
35 module_name = file_name.substring(0, file_name.search(".nit").from)
36 self.java_class = jclass
37 self.with_attributes = with_attributes
38 self.comment_unknown_types = comment
39 end
40
41 fun generate
42 do
43 var jclass = self.java_class
44
45 var class_content = new Array[String]
46 class_content.add(gen_class_header(jclass.class_type))
47
48 if with_attributes then
49 for id, jtype in jclass.attributes do class_content.add(gen_attribute(id, jtype))
50 end
51
52 for id, methods_info in jclass.methods do
53 for method_info in methods_info do
54 var nid = id
55 if methods_info.length > 1 then nid += "{methods_info.index_of(method_info)}"
56 class_content.add gen_method(id, nid, method_info.return_type, method_info.params)
57 end
58 end
59 class_content.add("\nend\n")
60
61 var wrappers = new Array[String]
62 for jtype in jclass.unknown_types do
63 if jtype == jclass.class_type then continue
64 wrappers.add("\n")
65 wrappers.add(gen_unknown_class_header(jtype))
66 end
67
68 var imports = new Array[String]
69 imports.add("import mnit_android\n")
70 for import_ in jclass.imports do
71 imports.add("import android::{import_}\n")
72 end
73
74 file_out.write(gen_licence)
75 file_out.write("module {module_name}\n")
76 file_out.write(imports.join(""))
77 file_out.write("\n")
78 file_out.write(class_content.join(""))
79 file_out.write(wrappers.join(""))
80 end
81
82 fun gen_licence: String
83 do
84 return """# This file is part of NIT (http://www.nitlanguage.org).
85 #
86 # Copyright [Year] [Author name] <Author e-mail>
87 #
88 # Licensed under the Apache License, Version 2.0 (the "License");
89 # you may not use this file except in compliance with the License.
90 # You may obtain a copy of the License at
91 #
92 # http://www.apache.org/licenses/LICENSE-2.0
93 #
94 # Unless required by applicable law or agreed to in writing, software
95 # distributed under the License is distributed on an "AS IS" BASIS,
96 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97 # See the License for the specific language governing permissions and
98 # limitations under the License.
99
100 # This code has been generated using `javap`
101 """
102 end
103
104 fun gen_class_header(jtype: JavaType): String
105 do
106 var temp = new Array[String]
107 temp.add("extern class Native{jtype.id} in \"Java\" `\{ {jtype} `\}\n")
108 temp.add("\tsuper JavaObject\n\n")
109
110 return temp.join("")
111 end
112
113 fun gen_unknown_class_header(jtype: JavaType): String
114 do
115 var nit_type: NitType
116 if jtype.extern_name.has_generic_params then
117 nit_type = jtype.extern_name.generic_params.first
118 else
119 nit_type = jtype.extern_name
120 end
121
122 var temp = new Array[String]
123 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
124 temp.add("\tsuper JavaObject\n\nend\n")
125
126 return temp.join("")
127 end
128
129 fun gen_attribute(jid: String, jtype: JavaType): String
130 do
131 return "\tvar {jid.to_nit_method_name}: {jtype.to_nit_type}\n"
132 end
133
134 fun gen_method(jmethod_id: String, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
135 do
136 var java_params = ""
137 var nit_params = ""
138 var nit_id = "arg"
139 var nit_id_no = 0
140 var nit_types = new Array[NitType]
141 var comment = ""
142
143 # Parameters
144 for i in [0..jparam_list.length[ do
145 var jparam = jparam_list[i]
146 var nit_type = jparam.to_nit_type
147
148 if not nit_type.is_complete then
149 if jparam.is_wrapped then
150 java_class.imports.add nit_type.mod.as(not null)
151 else
152 if comment_unknown_types then
153 comment = "#"
154 else
155 nit_type = jparam.extern_name
156 java_class.unknown_types.add(jparam)
157 end
158 end
159 end
160
161 var cast = ""
162
163 if not jparam.is_collection then cast = jparam.param_cast
164
165 nit_types.add(nit_type)
166 nit_type.arg_id = "{nit_id}{nit_id_no}"
167
168 if i == jparam_list.length - 1 then
169 java_params += "{cast}{nit_id}{nit_id_no}"
170 nit_params += "{nit_id}{nit_id_no}: {nit_type}"
171 else
172 java_params += "{cast}{nit_id}{nit_id_no}" + ", "
173 nit_params += "{nit_id}{nit_id_no}: {nit_type}, "
174 end
175
176 nit_id_no += 1
177 end
178
179 # Method identifier
180 var method_id = nmethod_id.to_nit_method_name
181 var nit_signature = new Array[String]
182
183 nit_signature.add "\tfun {method_id}"
184
185 if not jparam_list.is_empty then
186 nit_signature.add "({nit_params})"
187 end
188
189 var return_type = null
190
191 if not jreturn_type.is_void then
192 return_type = jreturn_type.to_nit_type
193
194 if not return_type.is_complete then
195 if jreturn_type.is_wrapped then
196 java_class.imports.add return_type.mod.as(not null)
197 else
198 if comment_unknown_types then
199 comment = "#"
200 else
201 return_type = jreturn_type.extern_name
202 java_class.unknown_types.add(jreturn_type)
203 end
204 end
205 end
206
207 nit_signature.add ": {return_type} "
208 end
209
210 var temp = new Array[String]
211
212 temp.add(comment + nit_signature.join(""))
213
214 # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it
215 if comment == "#" then
216 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
217 # Methods with return type
218 else if return_type != null then
219 temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast} recv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
220 # Methods without return type
221 else if jreturn_type.is_void then
222 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
223 # No copy
224 else
225 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
226 end
227
228 return temp.join("")
229 end
230 end
231
232 # Contains raw code mostly used to copy collections
233 class CodeWarehouse
234
235 private fun create_imports(nit_type: NitType, is_param: Bool): String
236 do
237 var imports = ""
238 var ntype = nit_type.to_s
239 var gen_type = nit_type.generic_params.join(", ")
240
241 if not is_param then
242 if nit_type.is_map then
243 imports = """ import {{{ntype}}}, {{{ntype}}}.[]="""
244 else
245 imports = """ import {{{ntype}}}, {{{ntype}}}.add"""
246 end
247 else if nit_type.id == "Array" then
248 imports = """ import {{{ntype}}}, {{{ntype}}}.length, {{{ntype}}}.[]"""
249 else if nit_type.is_map then
250 imports = """ import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item, Iterator[{{{gen_type}}}].key"""
251 else
252 imports = """ import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item"""
253 end
254
255 return imports
256 end
257 end
258
259 redef class String
260 # Convert the Java method name `self` to the Nit style
261 #
262 # * Converts to snake case
263 # * Strips `Get` and `Set`
264 # * Add suffix `=` to setters
265 fun to_nit_method_name: String
266 do
267 var name
268 if self.has_prefix("Get") then
269 name = self.substring_from(3)
270 else if self.has_prefix("Set") then
271 name = self.substring_from(3)
272 name += "="
273 else
274 name = self
275 end
276
277 return name.to_snake_case
278 end
279 end