6ea8974a5ac3d24ed7632db0d49baf3709025d6d
[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 # 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 # Services to generate extern class `in "Java"`
19 module code_generator
20
21 intrude import model
22
23 class CodeGenerator
24
25 # Path to the output file
26 var file_name: String
27
28 # Model of Java class being wrapped
29 var model: JavaModel
30
31 # Comment out methods with unknown (unwrapped) types
32 var comment_unknown_types: Bool
33
34 # Generate stub classes for unknown types used in the generated module
35 var stub_for_unknown_types: Bool
36
37 # Output file
38 var file_out: Writer = new FileWriter.open(file_name) is lazy, writable
39
40 # Name of the Nit module to generate
41 var module_name: nullable String is lazy do
42 if file_name.file_extension == "nit" then
43 # Output file ends with .nit, we expect it to be a valid name
44 return file_name.basename(".nit")
45 else return null
46 end
47
48 # Generate the Nit module into `file_out`
49 fun generate
50 do
51 # License
52 file_out.write license
53
54 # Module declaration
55 var module_name = module_name
56 if module_name != null then file_out.write "module {module_name}\n"
57 file_out.write "\n"
58
59 # All importations
60 var imports = new HashSet[String]
61 imports.add "import mnit_android\n"
62 for jclass in model.classes do
63 for import_ in jclass.imports do imports.add "import android::{import_}\n"
64 end
65 file_out.write imports.join("\n")
66
67 for jclass in model.classes do
68
69 file_out.write gen_class_header(jclass.class_type)
70
71 for id, signatures in jclass.methods do
72 var c = 0
73 for signature in signatures do
74 var nid = id
75 if c > 0 then nid += c.to_s
76 c += 1
77
78 file_out.write gen_method(jclass, id, nid, signature.return_type, signature.params)
79 file_out.write "\n"
80 end
81 end
82 file_out.write "end\n\n"
83 end
84
85 if stub_for_unknown_types then
86 for jtype in model.unknown_types do
87 file_out.write gen_unknown_class_header(jtype)
88 file_out.write "\n"
89 end
90 end
91 end
92
93 # License for the header of the generated Nit module
94 var license = """
95 # This file is part of NIT (http://www.nitlanguage.org).
96 #
97 # Licensed under the Apache License, Version 2.0 (the "License");
98 # you may not use this file except in compliance with the License.
99 # You may obtain a copy of the License at
100 #
101 # http://www.apache.org/licenses/LICENSE-2.0
102 #
103 # Unless required by applicable law or agreed to in writing, software
104 # distributed under the License is distributed on an "AS IS" BASIS,
105 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
106 # See the License for the specific language governing permissions and
107 # limitations under the License.
108
109 # This code has been generated using `jwrapper`
110 """ is writable
111
112 fun gen_class_header(jtype: JavaType): String
113 do
114 var temp = new Array[String]
115 temp.add("extern class Native{jtype.id} in \"Java\" `\{ {jtype} `\}\n")
116 temp.add("\tsuper JavaObject\n\n")
117
118 return temp.join
119 end
120
121 fun gen_unknown_class_header(jtype: JavaType): String
122 do
123 var nit_type: NitType
124 if jtype.extern_name.has_generic_params then
125 nit_type = jtype.extern_name.generic_params.first
126 else
127 nit_type = jtype.extern_name
128 end
129
130 var temp = new Array[String]
131 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
132 temp.add("\tsuper JavaObject\n\nend\n")
133
134 return temp.join
135 end
136
137 fun gen_method(java_class: JavaClass, jmethod_id, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
138 do
139 var java_params = ""
140 var nit_params = ""
141 var nit_id = "arg"
142 var nit_id_no = 0
143 var nit_types = new Array[NitType]
144 var comment = ""
145
146 # Parameters
147 for i in [0..jparam_list.length[ do
148 var jparam = jparam_list[i]
149 var nit_type = jparam.to_nit_type
150
151 if not nit_type.is_complete then
152 if jparam.is_wrapped then
153 java_class.imports.add nit_type.mod.as(not null)
154 else
155 model.unknown_types.add jparam
156 if comment_unknown_types then
157 comment = "#"
158 else
159 nit_type = jparam.extern_name
160 end
161 end
162 end
163
164 var cast = ""
165
166 if not jparam.is_collection then cast = jparam.param_cast
167
168 nit_types.add(nit_type)
169 nit_type.arg_id = "{nit_id}{nit_id_no}"
170
171 if i == jparam_list.length - 1 then
172 java_params += "{cast}{nit_id}{nit_id_no}"
173 nit_params += "{nit_id}{nit_id_no}: {nit_type}"
174 else
175 java_params += "{cast}{nit_id}{nit_id_no}" + ", "
176 nit_params += "{nit_id}{nit_id_no}: {nit_type}, "
177 end
178
179 nit_id_no += 1
180 end
181
182 # Method identifier
183 var method_id = nmethod_id.to_nit_method_name
184 var nit_signature = new Array[String]
185
186 nit_signature.add "\tfun {method_id}"
187
188 if not jparam_list.is_empty then
189 nit_signature.add "({nit_params})"
190 end
191
192 var return_type = null
193
194 if not jreturn_type.is_void then
195 return_type = jreturn_type.to_nit_type
196
197 if not return_type.is_complete then
198 if jreturn_type.is_wrapped then
199 java_class.imports.add return_type.mod.as(not null)
200 else
201 model.unknown_types.add jreturn_type
202 if comment_unknown_types then
203 comment = "#"
204 else
205 return_type = jreturn_type.extern_name
206 end
207 end
208 end
209
210 nit_signature.add ": {return_type} "
211 end
212
213 var temp = new Array[String]
214
215 temp.add(comment + nit_signature.join)
216
217 # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it
218 if comment == "#" then
219 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
220 # Methods with return type
221 else if return_type != null then
222 temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n")
223 # Methods without return type
224 else if jreturn_type.is_void then
225 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
226 # No copy
227 else
228 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
229 end
230
231 return temp.join
232 end
233 end
234
235 redef class String
236 # Convert the Java method name `self` to the Nit style
237 #
238 # * Converts to snake case
239 # * Strips `Get` and `Set`
240 # * Add suffix `=` to setters
241 fun to_nit_method_name: String
242 do
243 var name = self.to_snake_case
244 if name.has_prefix("get_") then
245 name = name.substring_from(4)
246 else if name.has_prefix("set_") then
247 name = name.substring_from(4) + "="
248 end
249
250 return name
251 end
252 end