1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 # Services to generate extern class `in "Java"`
24 var with_attributes
: Bool
25 var comment_unknown_types
: Bool
26 var file_out
: OFStream
27 var java_class
: JavaClass
29 var module_name
: String
30 fun code_warehouse
: CodeWarehouse do return once
new CodeWarehouse
32 init (file_name
: String, jclass
: JavaClass, with_attributes
, comment
: Bool)
34 file_out
= new OFStream.open
(file_name
)
35 module_name
= file_name
.substring
(0, file_name
.search
(".nit").from
)
36 self.java_class
= jclass
37 self.with_attributes
= with_attributes
38 self.comment_unknown_types
= comment
43 var jclass
= self.java_class
45 var class_content
= new Array[String]
46 class_content
.add
(gen_class_header
(jclass
.class_type
))
48 if with_attributes
then
49 for id
, jtype
in jclass
.attributes
do class_content
.add
(gen_attribute
(id
, jtype
))
52 for id
, methods_info
in jclass
.methods
do
53 for method_info
in methods_info
do
55 if methods_info
.length
> 1 then nid
+= "{methods_info.index_of(method_info)}"
56 class_content
.add gen_method
(id
, nid
, method_info
.return_type
, method_info
.params
)
59 class_content
.add
("\nend\n")
61 var wrappers
= new Array[String]
62 for jtype
in jclass
.unknown_types
do
63 if jtype
== jclass
.class_type
then continue
65 wrappers
.add
(gen_unknown_class_header
(jtype
))
68 var imports
= new Array[String]
69 imports
.add
("import mnit_android\n")
70 for import_
in jclass
.imports
do
71 imports
.add
("import android::{import_}\n")
74 file_out
.write
(gen_licence
)
75 file_out
.write
("module {module_name}\n")
76 file_out
.write
(imports
.join
(""))
78 file_out
.write
(class_content
.join
(""))
79 file_out
.write
(wrappers
.join
(""))
82 fun gen_licence
: String
84 return """# This file is part of NIT (http://www.nitlanguage.org).
86 # Copyright [Year] [Author name] <Author e-mail>
88 # Licensed under the Apache License, Version 2.0 (the "License");
89 # you may not use this file except in compliance with the License.
90 # You may obtain a copy of the License at
92 # http://www.apache.org/licenses/LICENSE-2.0
94 # Unless required by applicable law or agreed to in writing, software
95 # distributed under the License is distributed on an "AS IS" BASIS,
96 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
97 # See the License for the specific language governing permissions and
98 # limitations under the License.
100 # This code has been generated using `javap`
104 fun gen_class_header
(jtype
: JavaType): String
106 var temp
= new Array[String]
107 temp
.add
("extern class Native{jtype.id} in \"Java\
" `\{ {jtype} `\}\n")
108 temp
.add
("\tsuper JavaObject\n\tredef type SELF: Native{jtype.id}\n\n")
113 fun gen_unknown_class_header
(jtype
: JavaType): String
115 var nit_type
: NitType
116 if jtype
.extern_name
.has_generic_params
then
117 nit_type
= jtype
.extern_name
.generic_params
.first
119 nit_type
= jtype
.extern_name
122 var temp
= new Array[String]
123 temp
.add
("extern class {nit_type} in \"Java\
" `\{ {jtype.to_package_name} `\}\n")
124 temp
.add
("\tsuper JavaObject\n\tredef type SELF: {nit_type}\n\nend\n")
129 fun gen_attribute
(jid
: String, jtype
: JavaType): String
131 return "\tvar {jid.to_snake_case}: {jtype.to_nit_type}\n"
134 fun gen_method
(jmethod_id
: String, nmethod_id
: String, jreturn_type
: JavaType, jparam_list
: Array[JavaType]): String
140 var nit_types
= new Array[NitType]
144 for i
in [0..jparam_list
.length
[ do
145 var jparam
= jparam_list
[i
]
146 var nit_type
= jparam
.to_nit_type
148 if not nit_type
.is_complete
then
149 if jparam
.is_wrapped
then
150 java_class
.imports
.add nit_type
.mod
.as(not null)
152 if comment_unknown_types
then
155 nit_type
= jparam
.extern_name
156 java_class
.unknown_types
.add
(jparam
)
163 if not jparam
.is_collection
then cast
= jparam
.param_cast
165 nit_types
.add
(nit_type
)
166 nit_type
.arg_id
= "{nit_id}{nit_id_no}"
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}"
172 java_params
+= "{cast}{nit_id}{nit_id_no}" + ", "
173 nit_params
+= "{nit_id}{nit_id_no}: {nit_type}, "
180 var method_id
= nmethod_id
.to_snake_case
181 var nit_signature
= new Array[String]
183 nit_signature
.add
"\tfun {method_id}"
185 if not jparam_list
.is_empty
then
186 nit_signature
.add
"({nit_params})"
189 var return_type
= null
191 if not jreturn_type
.is_void
then
192 return_type
= jreturn_type
.to_nit_type
194 if not return_type
.is_complete
then
195 if jreturn_type
.is_wrapped
then
196 java_class
.imports
.add return_type
.mod
.as(not null)
198 if comment_unknown_types
then
201 return_type
= jreturn_type
.extern_name
202 java_class
.unknown_types
.add
(jreturn_type
)
207 nit_signature
.add
": {return_type} "
210 var param_to_copy
= param_to_copy
(jparam_list
, nit_types
)
212 var temp
= new Array[String]
214 if nb_params
> 1 then
216 temp
.add
("\t# NOT SUPPORTED: more than one parameter to copy\n")
217 temp
.add
("\t# Has to be implemented manually\n")
220 temp
.add
(comment
+ nit_signature
.join
(""))
222 # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it
223 if comment
== "#" then
224 temp
.add
(" in \"Java\
" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
225 # Methods with return type
226 else if return_type
!= null then
227 if jreturn_type
.is_primitive_array
then
228 # Copy one parameter and the return value
229 if param_to_copy
!= null then
230 var rtype_couple
= new Couple[JavaType, NitType](jreturn_type
, return_type
)
231 temp
.add
(code_warehouse
.param_return_copy
(rtype_couple
, param_to_copy
, jmethod_id
, java_params
))
232 # Copy the return type
234 temp
.add
(code_warehouse
.return_type_copy
(jreturn_type
, return_type
, jmethod_id
, java_params
))
237 else if param_to_copy
!= null then
238 temp
.add
(code_warehouse
.param_type_copy
(param_to_copy
.first
, param_to_copy
.second
, jmethod_id
, java_params
, true))
241 temp
.add
(" in \"Java\
" `\{\n{comment}\t\treturn {jreturn_type.return_cast} recv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
243 # Methods without return type
244 else if jreturn_type
.is_void
then
246 if param_to_copy
!= null then
247 temp
.add
(code_warehouse
.param_type_copy
(param_to_copy
.first
, param_to_copy
.second
, jmethod_id
, java_params
, false))
250 temp
.add
(" in \"Java\
" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
254 temp
.add
(" in \"Java\
" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n")
260 # Only one primitive array parameter can be copied
261 # If there's none or more than one then `null` is returned
262 fun param_to_copy
(jtypes
: Array[JavaType], ntypes
: Array[NitType]): nullable Couple[JavaType, NitType]
266 for i
in [0..jtypes
.length
[ do
267 if jtypes
[i
].is_primitive_array
then
269 couple
= new Couple[JavaType, NitType](jtypes
[i
], ntypes
[i
])
275 if counter
> 1 then return null
280 # Contains raw code mostly used to copy collections
283 # Collection as return value
284 fun return_type_copy
(java_type
: JavaType, nit_type
: NitType, jmethod_id
, params_id
: String): String
286 var narray_id
= "nit_array"
287 var loop_
= create_loop
(java_type
, nit_type
, false, "java_array", narray_id
)
288 var imports
= create_imports
(nit_type
, false)
290 return """{{{imports}}} in "Java" `{
291 {{{java_type.to_s}}} java_array = recv.{{{jmethod_id}}}({{{params_id}}});
292 int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
296 return {{{narray_id}}};
301 # Collection as parameter
302 fun param_type_copy
(java_type
: JavaType, nit_type
: NitType, jmethod_id
, params_id
: String, has_return
: Bool): String
304 var narray_id
= "nit_array"
305 var jarray_id
= "java_array"
306 var loop_
= create_loop
(java_type
, nit_type
, true, jarray_id
, narray_id
)
307 var imports
= create_imports
(nit_type
, true)
308 var jtype
= java_type
.to_s
309 var jinstanciation
= create_array_instance
(java_type
, nit_type
, jarray_id
)
313 return_str
= "return "
316 params_id
= params_id
.replace
(nit_type
.arg_id
, jarray_id
)
318 return """{{{imports}}} in "Java" `{
320 int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
324 {{{return_str}}}recv.{{{jmethod_id}}}({{{params_id}}});
329 # One collection parameter and the return type will be copied
330 fun param_return_copy
(return_types
, param_types
: Couple[JavaType, NitType], jmethod_id
, params_id
: String): String
332 var narray_id
= "nit_array"
333 var narray_id2
= "nit_array2"
335 var r_jtype
= return_types
.first
336 var r_ntype
= return_types
.second
338 var p_jtype
= param_types
.first
339 var p_ntype
= param_types
.second
341 var r_loop
= create_loop
(r_jtype
, r_ntype
, false, "java_array", narray_id
)
342 var p_loop
= create_loop
(p_jtype
, p_ntype
, true, "java_array2", narray_id2
)
344 var imports
= new Array[String]
346 # Avoid import duplication
347 if p_ntype
.to_s
!= r_ntype
.to_s
then
348 imports
.add create_imports
(p_ntype
, true)
351 imports
.add create_imports
(r_ntype
, false)
353 params_id
= params_id
.replace
(p_ntype
.arg_id
, narray_id
)
355 var jinstanciation
= create_array_instance
(p_jtype
, p_ntype
, "java_array")
357 return """{{{imports.join(", ")}}} in "Java" `{
362 {{{r_jtype.to_s}}} java_array2 = recv.{{{jmethod_id}}}({{{params_id}}});
363 int {{{narray_id2}}} = new_{{{r_ntype.id}}}_of_{{{r_ntype.generic_params.join("_")}}}();
367 return {{{narray_id2}}};
372 private fun create_array_instance
(java_type
: JavaType, nit_type
: NitType, jarray_id
: String): String
374 var jtype
= java_type
.to_s
375 var instanciation
= ""
377 if java_type
.is_primitive_array
then
378 instanciation
= "{jtype} {jarray_id} = new {java_type.full_id}[(int)Array_of_{nit_type.generic_params[0]}_length({nit_type.arg_id})];"
380 instanciation
= "{jtype} {jarray_id} = new {jtype}();"
386 private fun create_imports
(nit_type
: NitType, is_param
: Bool): String
389 var ntype
= nit_type
.to_s
390 var gen_type
= nit_type
.generic_params
.join
(", ")
393 if nit_type
.is_map
then
394 imports
= """ import {{{ntype}}}, {{{ntype}}}.[]="""
396 imports
= """ import {{{ntype}}}, {{{ntype}}}.add"""
398 else if nit_type
.id
== "Array" then
399 imports
= """ import {{{ntype}}}, {{{ntype}}}.length, {{{ntype}}}.[]"""
400 else if nit_type
.is_map
then
401 imports
= """ import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item, Iterator[{{{gen_type}}}].key"""
403 imports
= """ import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item"""
409 private fun create_loop
(java_type
: JavaType, nit_type
: NitType, is_param
: Bool, jarray_id
, narray_id
: String): String
413 var gen_type
= nit_type
.generic_params
.join
("_")
416 if java_type
.is_primitive_array
then
417 loop_header
= "for(int i=0; i < {jarray_id}.length; ++i)"
418 loop_body
= """\t\t\t{{{jarray_id}}}[i] = {{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{nit_type.arg_id}}}, i);"""
419 else if nit_type
.id
== "Array" then
420 loop_header
= """int length = Array_of_{{{gen_type}}}_length((int){{{nit_type.arg_id}}});\n\t\tfor(int i=0; i < length; ++i)"""
421 loop_body
= """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{narray_id}}}, i));"""
423 loop_header
= """int itr = {{{nit_type.id}}}_of_{{{gen_type}}}_iterator({{{nit_type.arg_id}}});\n\t\twhile(Iterator_of_{{{gen_type}}}_is_ok(itr)) {"""
424 if nit_type
.is_map
then
425 var key_cast
= java_type
.to_cast
(java_type
.generic_params
[0].id
, true)
426 var value_cast
= java_type
.to_cast
(java_type
.generic_params
[1].id
, true)
427 loop_body
= """\t\t\t{{{jarray_id}}}[{{{key_cast}}}iterator_of_{{{nit_type.id}}}_key(itr)] = {{{value_cast}}}iterator_of_{{{nit_type.id}}}_item(itr);\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
429 loop_body
= """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}iterator_of_{{{nit_type.id}}}_item(itr));\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
433 if nit_type
.is_map
then
434 var key_cast
= java_type
.to_cast
(java_type
.generic_params
[0].id
, false)
435 var value_cast
= java_type
.to_cast
(java_type
.generic_params
[1].id
, false)
436 loop_header
= """for (java.util.Map.Entry<{{{java_type.generic_params[0]}}}, {{{java_type.generic_params[1]}}}> e: {{{jarray_id}}})"""
437 loop_body
= """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_{{{nit_type.generic_params[1]}}}__index_assign({{{narray_id}}}, {{{key_cast}}}e.getKey(), {{{value_cast}}}e.getValue());"""
438 else if java_type
.is_iterable
then
439 loop_header
= """for ({{{java_type.generic_params[0]}}} e: {{{jarray_id}}})"""
440 loop_body
= """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}e);"""
442 loop_header
= "for(int i=0; i < {jarray_id}.length; ++i)"
443 loop_body
= """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}{{{jarray_id}}}[i]);"""
447 return loop_header
+ "\n" + loop_body