2d6012c89d7b2dc80efd6f67cb8c043676b97e9a
[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 java\n"
62 for key, 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 file_out.write "\n"
67
68 for key, jclass in model.classes do
69
70 file_out.write gen_class_header(jclass.class_type)
71
72 for id, signatures in jclass.methods do
73 var c = 0
74 for signature in signatures do
75 var nid = id
76 if c > 0 then nid += c.to_s
77 c += 1
78
79 file_out.write gen_method(jclass, id, nid, signature.return_type, signature.params)
80 file_out.write "\n"
81 end
82 end
83 file_out.write "end\n\n"
84 end
85
86 if stub_for_unknown_types then
87 for jtype in model.unknown_types do
88 file_out.write gen_unknown_class_header(jtype)
89 file_out.write "\n"
90 end
91 end
92 end
93
94 # License for the header of the generated Nit module
95 var license = """
96 # This file is part of NIT (http://www.nitlanguage.org).
97 #
98 # Licensed under the Apache License, Version 2.0 (the "License");
99 # you may not use this file except in compliance with the License.
100 # You may obtain a copy of the License at
101 #
102 # http://www.apache.org/licenses/LICENSE-2.0
103 #
104 # Unless required by applicable law or agreed to in writing, software
105 # distributed under the License is distributed on an "AS IS" BASIS,
106 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
107 # See the License for the specific language governing permissions and
108 # limitations under the License.
109
110 # This code has been generated using `jwrapper`
111 """ is writable
112
113 fun gen_class_header(jtype: JavaType): String
114 do
115 var temp = new Array[String]
116 var nit_type = jtype.to_nit_type
117 temp.add "# Java class: {jtype.to_package_name}\n"
118 temp.add "extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n"
119 temp.add "\tsuper JavaObject\n\n"
120
121 return temp.join
122 end
123
124 fun gen_unknown_class_header(jtype: JavaType): String
125 do
126 var nit_type = jtype.extern_name
127
128 var temp = new Array[String]
129 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
130 temp.add("\tsuper JavaObject\n\nend\n")
131
132 return temp.join
133 end
134
135 fun gen_method(java_class: JavaClass, jmethod_id, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
136 do
137 var java_params = ""
138 var nit_params = ""
139 var nit_id = "arg"
140 var nit_id_no = 0
141 var nit_types = new Array[NitType]
142 var comment = ""
143
144 # Parameters
145 for i in [0..jparam_list.length[ do
146 var jparam = jparam_list[i]
147 var nit_type = jparam.to_nit_type
148
149 if not nit_type.is_complete then
150 if jparam.is_wrapped then
151 java_class.imports.add nit_type.mod.as(not null)
152 else
153 model.unknown_types.add jparam
154 if comment_unknown_types then
155 comment = "#"
156 else
157 nit_type = jparam.extern_name
158 end
159 end
160 end
161
162 var cast = ""
163
164 if not jparam.is_collection then cast = jparam.param_cast
165
166 nit_types.add(nit_type)
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 documentation
180 var doc = "\t# Java implementation: {java_class}.{jmethod_id}\n"
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 doc
216 temp.add(comment + nit_signature.join)
217
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 Sys
236 # List of Nit keywords
237 #
238 # These may also be keywords in Java, but there they would be used capitalized.
239 private var nit_keywords: Array[String] = ["abort", "abstract", "and", "assert",
240 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
241 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
242 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
243 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while"]
244 end
245
246 redef class String
247
248 # Convert the Java method name `self` to the Nit style
249 #
250 # * Converts to snake case
251 # * Strips `Get` and `Set`
252 # * Add suffix `=` to setters
253 fun to_nit_method_name: String
254 do
255 var name = self.to_snake_case
256 if name.has_prefix("get_") then
257 name = name.substring_from(4)
258 else if name.has_prefix("set_") then
259 name = name.substring_from(4)
260 if nit_keywords.has(name) then name += "_"
261 name += "="
262 end
263
264 # Strip the '_' prefix
265 while name.has_prefix("_") do name = name.substring(1, name.length-1)
266
267 # Escape Nit keywords
268 if nit_keywords.has(name) then name += "_"
269
270 # If the name starts by something other than a letter, prefix with `java_`
271 if not name.chars.first.is_letter then name = "java_" + name
272
273 name = name.replace("$", "_")
274
275 return name
276 end
277 end