1 # This file is part of NIT (http://www.nitlanguage.org).
3 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
4 # Copyright 2015 Alexis Laferrière <alexis.laf@xymus.net>
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
10 # http://www.apache.org/licenses/LICENSE-2.0
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.
18 # Services to generate extern class `in "Java"`
25 # Path to the output file
28 # Model of Java class being wrapped
31 # Comment out methods with unknown (unwrapped) types
32 var comment_unknown_types
: Bool
34 # Generate stub classes for unknown types used in the generated module
35 var stub_for_unknown_types
: Bool
38 var file_out
: Writer = new FileWriter.open
(file_name
) is lazy
, writable
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")
48 # Generate the Nit module into `file_out`
52 file_out
.write license
55 var module_name
= module_name
56 if module_name
!= null then file_out
.write
"module {module_name}\n"
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"
65 file_out
.write imports
.join
("\n")
68 for key
, jclass
in model
.classes
do
70 generate_class_header
(jclass
.class_type
)
72 for id
, signatures
in jclass
.methods
do
73 for signature
in signatures
do if not signature
.is_static
then
74 generate_method
(jclass
, id
, id
, signature
.return_type
, signature
.params
)
80 for constructor
in jclass
.constructors
do
81 var complex
= jclass
.constructors
.length
!= 1 and constructor
.params
.not_empty
82 var base_name
= if complex
then "from" else ""
83 var name
= jclass
.nit_name_for
(base_name
, constructor
.params
, complex
)
85 generate_constructor
(jclass
, constructor
, name
)
89 for id
, attribute
in jclass
.attributes
do if not attribute
.is_static
then
90 generate_getter_setter
(jclass
, id
, attribute
)
94 generate_jni_services jclass
.class_type
97 file_out
.write
"end\n\n"
99 # Static functions as top-level methods
100 var static_functions_prefix
= jclass
.class_type
.extern_name
.to_snake_case
101 for id
, signatures
in jclass
.methods
do
102 for signature
in signatures
do if signature
.is_static
then
103 var nit_id
= static_functions_prefix
+ "_" + id
104 generate_method
(jclass
, id
, nit_id
, signature
.return_type
, signature
.params
, is_static
=true)
109 # Static attributes as top-level getters and setters
110 for id
, attribute
in jclass
.attributes
do if attribute
.is_static
then
111 generate_getter_setter
(jclass
, id
, attribute
)
115 for d
in [1..opt_arrays
.value
] do
116 generate_primitive_array
(jclass
, d
)
120 if stub_for_unknown_types
then
121 for jtype
, nit_type
in model
.unknown_types
do
122 generate_unknown_class_header
(jtype
)
128 # License for the header of the generated Nit module
130 # This file is part of NIT (http://www.nitlanguage.org).
132 # Licensed under the Apache License, Version 2.0 (the "License");
133 # you may not use this file except in compliance with the License.
134 # You may obtain a copy of the License at
136 # http://www.apache.org/licenses/LICENSE-2.0
138 # Unless required by applicable law or agreed to in writing, software
139 # distributed under the License is distributed on an "AS IS" BASIS,
140 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141 # See the License for the specific language governing permissions and
142 # limitations under the License.
144 # This code has been generated using `jwrapper`
147 private fun generate_class_header
(jtype
: JavaType)
149 var nit_type
= model
.java_to_nit_type
(jtype
)
150 file_out
.write
"# Java class: {jtype}\n"
151 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
152 file_out
.write
"\tsuper JavaObject\n\n"
155 private fun generate_unknown_class_header
(jtype
: JavaType)
157 var nit_type
= jtype
.extern_name
159 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
160 file_out
.write
"\tsuper JavaObject\n\nend\n"
163 private fun generate_method
(java_class
: JavaClass, java_method_id
, method_id
: String,
164 java_return_type
: JavaType, java_params
: Array[JavaType], is_static
: nullable Bool)
166 var java_args
= new Array[String]
167 var nit_params
= new Array[String]
173 for jparam
in java_params
do
174 var nit_type
= model
.java_to_nit_type
(jparam
)
176 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
178 java_args
.add
"{jparam.param_cast}{nit_id}{nit_id_no}"
179 nit_params
.add
"{nit_id}{nit_id_no}: {nit_type}"
185 method_id
= method_id
.to_nit_method_name
186 method_id
= java_class
.nit_name_for
(method_id
, java_params
, java_class
.methods
[java_method_id
].length
> 1)
188 # Build the signature
189 var nit_signature
= new Array[String]
190 nit_signature
.add
"fun {method_id}"
191 if not java_params
.is_empty
then nit_signature
.add
"({nit_params.join(", ")})"
194 var return_type
= null
195 if not java_return_type
.is_void
then
196 return_type
= model
.java_to_nit_type
(java_return_type
)
198 if not return_type
.is_known
and comment_unknown_types
then c
= "#"
200 nit_signature
.add
": " + return_type
.to_s
203 # Build the call in Java
205 if is_static
== true then
206 java_call
= java_class
.class_type
.package_name
207 else java_call
= "self"
208 java_call
+= ".{java_method_id}({java_args.join(", ")})"
210 if return_type
!= null then java_call
= "return {java_return_type.return_cast}" + java_call
214 if is_static
== true then t
= ""
219 {{{t}}}# Java implementation: {{{java_return_type}}} {{{java_class}}}.{{{java_method_id}}}({{{java_params.join(", ")}}})
220 {{{ct}}}{{{nit_signature.join}}} in "Java" `{
221 {{{ct}}} {{{java_call}}};
226 # Generate getter and setter to access an attribute, of field
227 private fun generate_getter_setter
(java_class
: JavaClass, java_id
: String,
228 attribute
: JavaAttribute)
230 var java_type
= attribute
.java_type
231 var nit_type
= model
.java_to_nit_type
(java_type
)
234 if attribute
.is_static
then nit_id
= java_class
.class_type
.extern_name
.to_snake_case
+ "_" + nit_id
235 nit_id
= nit_id
.to_nit_method_name
236 nit_id
= java_class
.nit_name_for
(nit_id
, [java_type
], false)
239 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
242 if attribute
.is_static
then
243 recv
= java_class
.class_type
.package_name
248 if attribute
.is_static
then t
= ""
252 {{{t}}}# Java getter: {{{java_class}}}.{{{java_id}}}
253 {{{ct}}}fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
254 {{{ct}}} return {{{recv}}}.{{{java_id}}};
257 {{{t}}}# Java setter: {{{java_class}}}.{{{java_id}}}
258 {{{ct}}}fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
259 {{{ct}}} {{{recv}}}.{{{java_id}}} = value;
265 # Generate getter and setter to access an attribute, of field
266 private fun generate_constructor
(java_class
: JavaClass, constructor
: JavaConstructor, name
: String)
269 var nit_params_s
= ""
270 var java_params_s
= ""
272 if constructor
.params
.not_empty
then
273 var nit_params
= new Array[String]
274 var java_params
= new Array[String]
276 for java_type
in constructor
.params
do
278 java_params
.add
"{java_type.param_cast}{param_id}"
280 var nit_type
= model
.java_to_nit_type
(java_type
)
281 nit_params
.add
"{param_id}: {nit_type}"
282 param_id
= param_id
.successor
(1)
284 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
287 nit_params_s
= "(" + nit_params
.join
(", ") + ")"
288 java_params_s
= java_params
.join
(", ")
292 # Java constructor: {{{java_class}}}
293 {{{c}}} new {{{name}}}{{{nit_params_s}}} in "Java" `{
294 {{{c}}} return new {{{java_class}}}({{{java_params_s}}});
300 private fun generate_primitive_array
(java_class
: JavaClass, dimensions
: Int)
302 var base_java_type
= java_class
.class_type
303 var java_type
= base_java_type
.clone
304 java_type
.array_dimension
= dimensions
306 var base_nit_type
= model
.java_to_nit_type
(base_java_type
)
307 var nit_type
= model
.java_to_nit_type
(java_type
)
310 # Java primitive array: {{{java_type}}}
311 extern class {{{nit_type}}} in "Java" `{ {{{java_type.extern_equivalent}}} `}
312 super AbstractJavaArray[{{{base_nit_type}}}]
314 # Get a new array of the given `size`
315 new(size: Int) in "Java" `{ return new {{{base_java_type}}}[(int)size]; `}
317 redef fun [](i) in "Java" `{ return self[(int)i]; `}
319 redef fun []=(i, e) in "Java" `{ self[(int)i] = e; `}
321 redef fun length in "Java" `{ return self.length; `}
324 generate_jni_services
(java_type
)
331 # Generate JNI related services
333 # For now, mostly avoid issue #845, but more services could be generated as needed.
334 private fun generate_jni_services
(java_type
: JavaType)
336 var nit_type
= model
.java_to_nit_type
(java_type
)
339 redef fun new_global_ref import sys, Sys.jni_env `{
340 Sys sys = {{{nit_type}}}_sys(self);
341 JNIEnv *env = Sys_jni_env(sys);
342 return (*env)->NewGlobalRef(env, self);
345 redef fun pop_from_local_frame_with_env(jni_env) `{
346 return (*jni_env)->PopLocalFrame(jni_env, self);
353 # List of Nit keywords
355 # These may also be keywords in Java, but there they would be used capitalized.
356 private var nit_keywords
= new HashSet[String].from
(["abort", "abstract", "and", "assert",
357 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
358 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
359 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
360 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while",
363 "class_name", "get_time", "hash", "is_same_type", "is_same_instance", "output",
365 # Pointer or JavaObject methods
371 # Convert the Java method name `self` to the Nit style
373 # * Converts to snake case
374 # * Strips `Get` and `Set`
375 # * Add suffix `=` to setters
376 fun to_nit_method_name
: String
378 var name
= self.to_snake_case
380 # Strip the '_' prefix
381 while name
.has_prefix
("_") do name
= name
.substring
(1, name
.length-1
)
383 # Escape Nit keywords
384 if nit_keywords
.has
(name
) then name
+= "_"
386 # If the name starts by something other than a letter, prefix with `java_`
387 if not name
.chars
.first
.is_letter
then name
= "java_" + name
389 name
= name
.replace
("$", "_")
395 redef class JavaClass
396 # Property names used in this class
397 private var used_name
= new HashSet[String]
399 # Get an available property name for the Java property with `name` and parameters
401 # If `use_parameters_name` then expect that there will be conflicts,
402 # so use the types of `parameters` to build the name.
403 private fun nit_name_for
(name
: String, parameters
: Array[JavaType], use_parameters_name
: Bool): String
405 # Append the name of each parameter
406 if use_parameters_name
then
407 for param
in parameters
do
408 name
+= "_" + param
.id
412 # As a last resort, append numbers to the name
415 while used_name
.has
(name
) do
416 name
= base_name
+ count
.to_s