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