ed456f647f08827c8825dec4669c66ddf4811c1f
[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 """
84 # This file is part of NIT (http://www.nitlanguage.org).
85 #
86 # Licensed under the Apache License, Version 2.0 (the "License");
87 # you may not use this file except in compliance with the License.
88 # You may obtain a copy of the License at
89 #
90 # http://www.apache.org/licenses/LICENSE-2.0
91 #
92 # Unless required by applicable law or agreed to in writing, software
93 # distributed under the License is distributed on an "AS IS" BASIS,
94 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95 # See the License for the specific language governing permissions and
96 # limitations under the License.
97
98 # This code has been generated using `jwrapper`
99 """
100 end
101
102 fun gen_class_header(jtype: JavaType): String
103 do
104 var temp = new Array[String]
105 temp.add("extern class Native{jtype.id} in \"Java\" `\{ {jtype} `\}\n")
106 temp.add("\tsuper JavaObject\n\n")
107
108 return temp.join("")
109 end
110
111 fun gen_unknown_class_header(jtype: JavaType): String
112 do
113 var nit_type: NitType
114 if jtype.extern_name.has_generic_params then
115 nit_type = jtype.extern_name.generic_params.first
116 else
117 nit_type = jtype.extern_name
118 end
119
120 var temp = new Array[String]
121 temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
122 temp.add("\tsuper JavaObject\n\nend\n")
123
124 return temp.join("")
125 end
126
127 fun gen_attribute(jid: String, jtype: JavaType): String
128 do
129 return "\tvar {jid.to_nit_method_name}: {jtype.to_nit_type}\n"
130 end
131
132 fun gen_method(jmethod_id: String, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
133 do
134 var java_params = ""
135 var nit_params = ""
136 var nit_id = "arg"
137 var nit_id_no = 0
138 var nit_types = new Array[NitType]
139 var comment = ""
140
141 # Parameters
142 for i in [0..jparam_list.length[ do
143 var jparam = jparam_list[i]
144 var nit_type = jparam.to_nit_type
145
146 if not nit_type.is_complete then
147 if jparam.is_wrapped then
148 java_class.imports.add nit_type.mod.as(not null)
149 else
150 if comment_unknown_types then
151 comment = "#"
152 else
153 nit_type = jparam.extern_name
154 java_class.unknown_types.add(jparam)
155 end
156 end
157 end
158
159 var cast = ""
160
161 if not jparam.is_collection then cast = jparam.param_cast
162
163 nit_types.add(nit_type)
164 nit_type.arg_id = "{nit_id}{nit_id_no}"
165
166 if i == jparam_list.length - 1 then
167 java_params += "{cast}{nit_id}{nit_id_no}"
168 nit_params += "{nit_id}{nit_id_no}: {nit_type}"
169 else
170 java_params += "{cast}{nit_id}{nit_id_no}" + ", "
171 nit_params += "{nit_id}{nit_id_no}: {nit_type}, "
172 end
173
174 nit_id_no += 1
175 end
176
177 # Method identifier
178 var method_id = nmethod_id.to_nit_method_name
179 var nit_signature = new Array[String]
180
181 nit_signature.add "\tfun {method_id}"
182
183 if not jparam_list.is_empty then
184 nit_signature.add "({nit_params})"
185 end
186
187 var return_type = null
188
189 if not jreturn_type.is_void then
190 return_type = jreturn_type.to_nit_type
191
192 if not return_type.is_complete then
193 if jreturn_type.is_wrapped then
194 java_class.imports.add return_type.mod.as(not null)
195 else
196 if comment_unknown_types then
197 comment = "#"
198 else
199 return_type = jreturn_type.extern_name
200 java_class.unknown_types.add(jreturn_type)
201 end
202 end
203 end
204
205 nit_signature.add ": {return_type} "
206 end
207
208 var temp = new Array[String]
209
210 temp.add(comment + nit_signature.join(""))
211
212 # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it
213 if comment == "#" then
214 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
215 # Methods with return type
216 else if return_type != null then
217 temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}recv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
218 # Methods without return type
219 else if jreturn_type.is_void then
220 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
221 # No copy
222 else
223 temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
224 end
225
226 return temp.join("")
227 end
228 end
229
230 redef class String
231 # Convert the Java method name `self` to the Nit style
232 #
233 # * Converts to snake case
234 # * Strips `Get` and `Set`
235 # * Add suffix `=` to setters
236 fun to_nit_method_name: String
237 do
238 var name = self.to_snake_case
239 if name.has_prefix("get_") then
240 name = name.substring_from(4)
241 else if name.has_prefix("set_") then
242 name = name.substring_from(4) + "="
243 end
244
245 return name
246 end
247 end