7a4e419e9aed5da24ef5a5ae48a74e14d9de233f
[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 java_class: JavaClass
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 var jclass = self.java_class
52
53 var class_content = new Array[String]
54 class_content.add(gen_class_header(jclass.class_type))
55
56 for id, methods_info in jclass.methods do
57 for method_info in methods_info do
58 var nid = id
59 if methods_info.length > 1 then nid += "{methods_info.index_of(method_info)}"
60 class_content.add gen_method(id, nid, method_info.return_type, method_info.params)
61 end
62 end
63 class_content.add("\nend\n")
64
65 var wrappers = new Array[String]
66 if stub_for_unknown_types then
67 for jtype in jclass.unknown_types do
68 if jtype == jclass.class_type then continue
69 wrappers.add("\n")
70 wrappers.add(gen_unknown_class_header(jtype))
71 end
72 end
73
74 var imports = new Array[String]
75 imports.add("import mnit_android\n")
76 for import_ in jclass.imports do
77 imports.add("import android::{import_}\n")
78 end
79
80 file_out.write(gen_licence)
81
82 var module_name = module_name
83 if module_name != null then file_out.write "module {module_name}\n"
84
85 file_out.write("\n")
86 file_out.write(imports.join)
87 file_out.write("\n")
88 file_out.write(class_content.join)
89 file_out.write(wrappers.join)
90 end
91
92 fun gen_licence: String
93 do
94 return """
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 """
111 end
112
113 fun gen_class_header(jtype: JavaType): String
114 do
115 var temp = new Array[String]
116 temp.add("extern class Native{jtype.id} in \"Java\" `\{ {jtype} `\}\n")
117 temp.add("\tsuper JavaObject\n\n")
118
119 return temp.join
120 end
121
122 fun gen_unknown_class_header(jtype: JavaType): String
123 do
124 var nit_type: NitType
125 if jtype.extern_name.has_generic_params then
126 nit_type = jtype.extern_name.generic_params.first
127 else
128 nit_type = jtype.extern_name
129 end
130
131 var temp = new Array[String]
132 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
133 temp.add("\tsuper JavaObject\n\nend\n")
134
135 return temp.join
136 end
137
138 fun gen_method(jmethod_id: String, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
139 do
140 var java_params = ""
141 var nit_params = ""
142 var nit_id = "arg"
143 var nit_id_no = 0
144 var nit_types = new Array[NitType]
145 var comment = ""
146
147 # Parameters
148 for i in [0..jparam_list.length[ do
149 var jparam = jparam_list[i]
150 var nit_type = jparam.to_nit_type
151
152 if not nit_type.is_complete then
153 if jparam.is_wrapped then
154 java_class.imports.add nit_type.mod.as(not null)
155 else
156 java_class.unknown_types.add jparam
157 if comment_unknown_types then
158 comment = "#"
159 else
160 nit_type = jparam.extern_name
161 end
162 end
163 end
164
165 var cast = ""
166
167 if not jparam.is_collection then cast = jparam.param_cast
168
169 nit_types.add(nit_type)
170 nit_type.arg_id = "{nit_id}{nit_id_no}"
171
172 if i == jparam_list.length - 1 then
173 java_params += "{cast}{nit_id}{nit_id_no}"
174 nit_params += "{nit_id}{nit_id_no}: {nit_type}"
175 else
176 java_params += "{cast}{nit_id}{nit_id_no}" + ", "
177 nit_params += "{nit_id}{nit_id_no}: {nit_type}, "
178 end
179
180 nit_id_no += 1
181 end
182
183 # Method identifier
184 var method_id = nmethod_id.to_nit_method_name
185 var nit_signature = new Array[String]
186
187 nit_signature.add "\tfun {method_id}"
188
189 if not jparam_list.is_empty then
190 nit_signature.add "({nit_params})"
191 end
192
193 var return_type = null
194
195 if not jreturn_type.is_void then
196 return_type = jreturn_type.to_nit_type
197
198 if not return_type.is_complete then
199 if jreturn_type.is_wrapped then
200 java_class.imports.add return_type.mod.as(not null)
201 else
202 java_class.unknown_types.add jreturn_type
203 if comment_unknown_types then
204 comment = "#"
205 else
206 return_type = jreturn_type.extern_name
207 end
208 end
209 end
210
211 nit_signature.add ": {return_type} "
212 end
213
214 var temp = new Array[String]
215
216 temp.add(comment + nit_signature.join)
217
218 # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it
219 if comment == "#" then
220 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
221 # Methods with return type
222 else if return_type != null then
223 temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n")
224 # Methods without return type
225 else if jreturn_type.is_void then
226 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
227 # No copy
228 else
229 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
230 end
231
232 return temp.join
233 end
234 end
235
236 redef class String
237 # Convert the Java method name `self` to the Nit style
238 #
239 # * Converts to snake case
240 # * Strips `Get` and `Set`
241 # * Add suffix `=` to setters
242 fun to_nit_method_name: String
243 do
244 var name = self.to_snake_case
245 if name.has_prefix("get_") then
246 name = name.substring_from(4)
247 else if name.has_prefix("set_") then
248 name = name.substring_from(4) + "="
249 end
250
251 return name
252 end
253 end