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
69 # Skip anonymous classes
70 if jclass
.class_type
.is_anonymous
then continue
72 generate_class_header
(jclass
.class_type
)
74 for id
, signatures
in jclass
.methods
do
75 for signature
in signatures
do if not signature
.is_static
then
76 generate_method
(jclass
, id
, id
, signature
.return_type
, signature
.params
)
82 for constructor
in jclass
.constructors
do
83 var complex
= jclass
.constructors
.length
!= 1 and constructor
.params
.not_empty
84 var base_name
= if complex
then "from" else ""
85 var name
= jclass
.nit_name_for
(base_name
, constructor
.params
, complex
, false)
87 generate_constructor
(jclass
, constructor
, name
)
91 for id
, attribute
in jclass
.attributes
do if not attribute
.is_static
then
92 generate_getter_setter
(jclass
, id
, attribute
)
96 generate_jni_services jclass
.class_type
99 file_out
.write
"end\n\n"
101 # Static functions as top-level methods
102 var static_functions_prefix
= jclass
.class_type
.extern_name
.to_snake_case
103 for id
, signatures
in jclass
.methods
do
104 for signature
in signatures
do if signature
.is_static
then
105 var nit_id
= static_functions_prefix
+ "_" + id
106 generate_method
(jclass
, id
, nit_id
, signature
.return_type
, signature
.params
, is_static
=true)
111 # Static attributes as top-level getters and setters
112 for id
, attribute
in jclass
.attributes
do if attribute
.is_static
then
113 generate_getter_setter
(jclass
, id
, attribute
)
117 for d
in [1..opt_arrays
.value
] do
118 generate_primitive_array
(jclass
, d
)
122 if stub_for_unknown_types
then
123 for jtype
, nit_type
in model
.unknown_types
do
124 generate_unknown_class_header
(jtype
)
130 # License for the header of the generated Nit module
132 # This file is part of NIT (http://www.nitlanguage.org).
134 # Licensed under the Apache License, Version 2.0 (the "License");
135 # you may not use this file except in compliance with the License.
136 # You may obtain a copy of the License at
138 # http://www.apache.org/licenses/LICENSE-2.0
140 # Unless required by applicable law or agreed to in writing, software
141 # distributed under the License is distributed on an "AS IS" BASIS,
142 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
143 # See the License for the specific language governing permissions and
144 # limitations under the License.
146 # This code has been generated using `jwrapper`
149 private fun generate_class_header
(jtype
: JavaType)
151 var nit_type
= model
.java_to_nit_type
(jtype
)
152 file_out
.write
"# Java class: {jtype}\n"
153 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
154 file_out
.write
"\tsuper JavaObject\n\n"
157 private fun generate_unknown_class_header
(jtype
: JavaType)
159 var nit_type
= jtype
.extern_name
161 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
162 file_out
.write
"\tsuper JavaObject\n\nend\n"
165 private fun generate_method
(java_class
: JavaClass, java_method_id
, method_id
: String,
166 java_return_type
: JavaType, java_params
: Array[JavaType], is_static
: nullable Bool)
168 var java_args
= new Array[String]
169 var nit_params
= new Array[String]
175 for jparam
in java_params
do
176 var nit_type
= model
.java_to_nit_type
(jparam
)
178 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
179 if jparam
.is_vararg
then c
= "#"
181 java_args
.add
"{jparam.param_cast}{nit_id}{nit_id_no}"
182 nit_params
.add
"{nit_id}{nit_id_no}: {nit_type}"
188 method_id
= method_id
.to_nit_method_name
189 method_id
= java_class
.nit_name_for
(method_id
, java_params
, java_class
.methods
[java_method_id
].length
> 1, is_static
== true)
191 # Build the signature
192 var nit_signature
= new Array[String]
193 nit_signature
.add
"fun {method_id}"
194 if not java_params
.is_empty
then nit_signature
.add
"({nit_params.join(", ")})"
197 var return_type
= null
198 if not java_return_type
.is_void
then
199 return_type
= model
.java_to_nit_type
(java_return_type
)
201 if not return_type
.is_known
and comment_unknown_types
then c
= "#"
202 if java_return_type
.is_vararg
then c
= "#"
204 nit_signature
.add
": " + return_type
.to_s
207 # Build the call in Java
209 if is_static
== true then
210 java_call
= java_class
.class_type
.package_name
211 else java_call
= "self"
212 java_call
+= ".{java_method_id}({java_args.join(", ")})"
214 if return_type
!= null then java_call
= "return {java_return_type.return_cast}" + java_call
218 if is_static
== true then t
= ""
223 {{{t}}}# Java implementation: {{{java_return_type}}} {{{java_class}}}.{{{java_method_id}}}({{{java_params.join(", ")}}})
224 {{{ct}}}{{{nit_signature.join}}} in "Java" `{
225 {{{ct}}} {{{java_call}}};
230 # Generate getter and setter to access an attribute, of field
231 private fun generate_getter_setter
(java_class
: JavaClass, java_id
: String,
232 attribute
: JavaAttribute)
234 var java_type
= attribute
.java_type
235 var nit_type
= model
.java_to_nit_type
(java_type
)
238 if attribute
.is_static
then nit_id
= java_class
.class_type
.extern_name
.to_snake_case
+ "_" + nit_id
239 nit_id
= nit_id
.to_nit_method_name
240 nit_id
= java_class
.nit_name_for
(nit_id
, [java_type
], false, attribute
.is_static
)
243 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
244 if java_type
.is_vararg
then c
= "#"
247 if attribute
.is_static
then
248 recv
= java_class
.class_type
.package_name
253 if attribute
.is_static
then t
= ""
257 {{{t}}}# Java getter: {{{java_class}}}.{{{java_id}}}
258 {{{ct}}}fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
259 {{{ct}}} return {{{recv}}}.{{{java_id}}};
262 {{{t}}}# Java setter: {{{java_class}}}.{{{java_id}}}
263 {{{ct}}}fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
264 {{{ct}}} {{{recv}}}.{{{java_id}}} = value;
270 # Generate getter and setter to access an attribute, of field
271 private fun generate_constructor
(java_class
: JavaClass, constructor
: JavaConstructor, name
: String)
274 var nit_params_s
= ""
275 var java_params_s
= ""
277 if constructor
.params
.not_empty
then
278 var nit_params
= new Array[String]
279 var java_params
= new Array[String]
281 for java_type
in constructor
.params
do
283 java_params
.add
"{java_type.param_cast}{param_id}"
285 var nit_type
= model
.java_to_nit_type
(java_type
)
286 nit_params
.add
"{param_id}: {nit_type}"
287 param_id
= param_id
.successor
(1)
289 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
290 if java_type
.is_vararg
then c
= "#"
293 nit_params_s
= "(" + nit_params
.join
(", ") + ")"
294 java_params_s
= java_params
.join
(", ")
298 # Java constructor: {{{java_class}}}
299 {{{c}}} new {{{name}}}{{{nit_params_s}}} in "Java" `{
300 {{{c}}} return new {{{java_class.class_type.package_name}}}({{{java_params_s}}});
306 private fun generate_primitive_array
(java_class
: JavaClass, dimensions
: Int)
308 var base_java_type
= java_class
.class_type
309 var java_type
= base_java_type
.clone
310 java_type
.array_dimension
= dimensions
312 var base_nit_type
= model
.java_to_nit_type
(base_java_type
)
313 var nit_type
= model
.java_to_nit_type
(java_type
)
316 # Java primitive array: {{{java_type}}}
317 extern class {{{nit_type}}} in "Java" `{ {{{java_type.extern_equivalent}}} `}
318 super AbstractJavaArray[{{{base_nit_type}}}]
320 # Get a new array of the given `size`
321 new(size: Int) in "Java" `{ return new {{{base_java_type}}}[(int)size]; `}
323 redef fun [](i) in "Java" `{ return self[(int)i]; `}
325 redef fun []=(i, e) in "Java" `{ self[(int)i] = e; `}
327 redef fun length in "Java" `{ return self.length; `}
330 generate_jni_services
(java_type
)
337 # Generate JNI related services
339 # For now, mostly avoid issue #845, but more services could be generated as needed.
340 private fun generate_jni_services
(java_type
: JavaType)
342 var nit_type
= model
.java_to_nit_type
(java_type
)
345 redef fun new_global_ref import sys, Sys.jni_env `{
346 Sys sys = {{{nit_type}}}_sys(self);
347 JNIEnv *env = Sys_jni_env(sys);
348 return (*env)->NewGlobalRef(env, self);
351 redef fun pop_from_local_frame_with_env(jni_env) `{
352 return (*jni_env)->PopLocalFrame(jni_env, self);
359 # List of Nit keywords
361 # These may also be keywords in Java, but there they would be used capitalized.
362 private var nit_keywords
= new HashSet[String].from
(["abort", "abstract", "and", "assert",
363 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
364 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
365 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
366 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while",
369 "class_name", "get_time", "hash", "inspect", "inspect_head", "is_same_type",
370 "is_same_instance", "object_id", "output", "output_class_name", "sys", "to_s",
372 # Pointer or JavaObject methods
375 # Name of methods used at the top-level
377 # Used by `JavaClass::nit_name_for` with static properties.
378 private var top_level_used_names
= new HashSet[String]
383 # Convert the Java method name `self` to the Nit style
385 # * Converts to snake case
386 # * Strips `Get` and `Set`
387 # * Add suffix `=` to setters
388 fun to_nit_method_name
: String
390 var name
= self.to_snake_case
392 # Strip the '_' prefix
393 while name
.has_prefix
("_") do name
= name
.substring
(1, name
.length-1
)
395 # Escape Nit keywords
396 if nit_keywords
.has
(name
) then name
+= "_"
398 # If the name starts by something other than a letter, prefix with `java_`
399 if not name
.chars
.first
.is_letter
then name
= "java_" + name
401 name
= name
.replace
("$", "_")
407 redef class JavaClass
408 # Property names used in this class
409 private var used_names
= new HashSet[String]
411 # Get an available property name for the Java property with `name` and parameters
413 # If `use_parameters_name` then expect that there will be conflicts,
414 # so use the types of `parameters` to build the name.
415 private fun nit_name_for
(name
: String, parameters
: Array[JavaType], use_parameters_name
: Bool, is_static
: Bool): String
417 # Append the name of each parameter
418 if use_parameters_name
then
419 for param
in parameters
do
420 name
+= "_" + param
.id
424 # Set of property names, local or top-level
427 used_names
= sys
.top_level_used_names
428 else used_names
= self.used_names
430 # As a last resort, append numbers to the name
433 while used_names
.has
(name
) do
434 name
= base_name
+ count
.to_s