contrib/jwrapper: remove NitType::arg_id it was unused, only set
[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 var nit_type = jtype.to_nit_type
116 temp.add "# Java class: {jtype.to_package_name}\n"
117 temp.add "extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n"
118 temp.add "\tsuper JavaObject\n\n"
119
120 return temp.join
121 end
122
123 fun gen_unknown_class_header(jtype: JavaType): String
124 do
125 var nit_type: NitType
126 if jtype.extern_name.has_generic_params then
127 nit_type = jtype.extern_name.generic_params.first
128 else
129 nit_type = jtype.extern_name
130 end
131
132 var temp = new Array[String]
133 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
134 temp.add("\tsuper JavaObject\n\nend\n")
135
136 return temp.join
137 end
138
139 fun gen_method(java_class: JavaClass, jmethod_id, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
140 do
141 var java_params = ""
142 var nit_params = ""
143 var nit_id = "arg"
144 var nit_id_no = 0
145 var nit_types = new Array[NitType]
146 var comment = ""
147
148 # Parameters
149 for i in [0..jparam_list.length[ do
150 var jparam = jparam_list[i]
151 var nit_type = jparam.to_nit_type
152
153 if not nit_type.is_complete then
154 if jparam.is_wrapped then
155 java_class.imports.add nit_type.mod.as(not null)
156 else
157 model.unknown_types.add jparam
158 if comment_unknown_types then
159 comment = "#"
160 else
161 nit_type = jparam.extern_name
162 end
163 end
164 end
165
166 var cast = ""
167
168 if not jparam.is_collection then cast = jparam.param_cast
169
170 nit_types.add(nit_type)
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 documentation
184 var doc = "\t# Java implementation: {java_class}.{jmethod_id}\n"
185
186 # Method identifier
187 var method_id = nmethod_id.to_nit_method_name
188 var nit_signature = new Array[String]
189
190 nit_signature.add "\tfun {method_id}"
191
192 if not jparam_list.is_empty then
193 nit_signature.add "({nit_params})"
194 end
195
196 var return_type = null
197
198 if not jreturn_type.is_void then
199 return_type = jreturn_type.to_nit_type
200
201 if not return_type.is_complete then
202 if jreturn_type.is_wrapped then
203 java_class.imports.add return_type.mod.as(not null)
204 else
205 model.unknown_types.add jreturn_type
206 if comment_unknown_types then
207 comment = "#"
208 else
209 return_type = jreturn_type.extern_name
210 end
211 end
212 end
213
214 nit_signature.add ": {return_type} "
215 end
216
217 var temp = new Array[String]
218
219 temp.add doc
220 temp.add(comment + nit_signature.join)
221
222 if comment == "#" then
223 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
224 # Methods with return type
225 else if return_type != null then
226 temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n")
227 # Methods without return type
228 else if jreturn_type.is_void then
229 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
230 # No copy
231 else
232 temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
233 end
234
235 return temp.join
236 end
237 end
238
239 redef class Sys
240 # List of Nit keywords
241 #
242 # These may also be keywords in Java, but there they would be used capitalized.
243 private var nit_keywords: Array[String] = ["abort", "abstract", "and", "assert",
244 "break", "class", "continue", "do", "else", "end", "enum", "extern", "implies",
245 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "for", "label",
246 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
247 "protected", "public", "return", "self", "super", "then", "type", "var", "while"]
248 end
249
250 redef class String
251
252 # Convert the Java method name `self` to the Nit style
253 #
254 # * Converts to snake case
255 # * Strips `Get` and `Set`
256 # * Add suffix `=` to setters
257 fun to_nit_method_name: String
258 do
259 var name = self.to_snake_case
260 if name.has_prefix("get_") then
261 name = name.substring_from(4)
262 else if name.has_prefix("set_") then
263 name = name.substring_from(4)
264 if nit_keywords.has(name) then name += "_"
265 name += "="
266 end
267
268 # Strip the '_' prefix
269 while name.has_prefix("_") do name = name.substring(1, name.length-1)
270
271 # Escape Nit keywords
272 if nit_keywords.has(name) then name += "_"
273
274 # If the name starts by something other than a letter, prefix with `java_`
275 if not name.chars.first.is_letter then name = "java_" + name
276
277 name = name.replace("$", "_")
278
279 return name
280 end
281 end