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 # Skip classes with an invalid name at the Java language level
73 if jclass
.class_type
.extern_equivalent
.has
("-") then continue
75 generate_class_header
(jclass
.class_type
)
77 for id
, signatures
in jclass
.methods
do
78 for signature
in signatures
do if not signature
.is_static
then
79 generate_method
(jclass
, id
, id
, signature
.return_type
, signature
.params
)
85 for constructor
in jclass
.constructors
do
86 var complex
= jclass
.constructors
.length
!= 1 and constructor
.params
.not_empty
87 var base_name
= if complex
then "from" else ""
88 var name
= jclass
.nit_name_for
(base_name
, constructor
.params
, complex
, false)
90 generate_constructor
(jclass
, constructor
, name
)
94 for id
, attribute
in jclass
.attributes
do if not attribute
.is_static
then
95 generate_getter_setter
(jclass
, id
, attribute
)
99 generate_jni_services jclass
.class_type
102 file_out
.write
"end\n\n"
104 # Static functions as top-level methods
105 var static_functions_prefix
= jclass
.class_type
.extern_name
.to_snake_case
106 for id
, signatures
in jclass
.methods
do
107 for signature
in signatures
do if signature
.is_static
then
108 var nit_id
= static_functions_prefix
+ "_" + id
109 generate_method
(jclass
, id
, nit_id
, signature
.return_type
, signature
.params
, is_static
=true)
114 # Static attributes as top-level getters and setters
115 for id
, attribute
in jclass
.attributes
do if attribute
.is_static
then
116 generate_getter_setter
(jclass
, id
, attribute
)
120 for d
in [1..opt_arrays
.value
] do
121 generate_primitive_array
(jclass
, d
)
125 if stub_for_unknown_types
then
126 for jtype
, nit_type
in model
.unknown_types
do
127 generate_unknown_class_header
(jtype
)
135 # License for the header of the generated Nit module
137 # This file is part of NIT (http://www.nitlanguage.org).
139 # Licensed under the Apache License, Version 2.0 (the "License");
140 # you may not use this file except in compliance with the License.
141 # You may obtain a copy of the License at
143 # http://www.apache.org/licenses/LICENSE-2.0
145 # Unless required by applicable law or agreed to in writing, software
146 # distributed under the License is distributed on an "AS IS" BASIS,
147 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
148 # See the License for the specific language governing permissions and
149 # limitations under the License.
151 # This code has been generated using `jwrapper`
154 private fun generate_class_header
(jtype
: JavaType)
156 var nit_type
= model
.java_to_nit_type
(jtype
)
157 file_out
.write
"# Java class: {jtype}\n"
158 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
159 file_out
.write
"\tsuper JavaObject\n\n"
162 private fun generate_unknown_class_header
(jtype
: JavaType)
164 var nit_type
= jtype
.extern_name
166 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
167 file_out
.write
"\tsuper JavaObject\n\nend\n"
170 private fun generate_method
(java_class
: JavaClass, java_method_id
, method_id
: String,
171 java_return_type
: JavaType, java_params
: Array[JavaType], is_static
: nullable Bool)
173 var java_args
= new Array[String]
174 var nit_params
= new Array[String]
180 for jparam
in java_params
do
181 var nit_type
= model
.java_to_nit_type
(jparam
)
183 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
184 if jparam
.is_vararg
then c
= "#"
186 java_args
.add
"{jparam.param_cast}{nit_id}{nit_id_no}"
187 nit_params
.add
"{nit_id}{nit_id_no}: {nit_type}"
193 method_id
= method_id
.to_nit_method_name
194 method_id
= java_class
.nit_name_for
(method_id
, java_params
, java_class
.methods
[java_method_id
].length
> 1, is_static
== true)
196 # Build the signature
197 var nit_signature
= new Array[String]
198 nit_signature
.add
"fun {method_id}"
199 if not java_params
.is_empty
then nit_signature
.add
"({nit_params.join(", ")})"
202 var return_type
= null
203 if not java_return_type
.is_void
then
204 return_type
= model
.java_to_nit_type
(java_return_type
)
206 if not return_type
.is_known
and comment_unknown_types
then c
= "#"
207 if java_return_type
.is_vararg
then c
= "#"
209 nit_signature
.add
": " + return_type
.to_s
212 # Build the call in Java
214 if is_static
== true then
215 java_call
= java_class
.class_type
.package_name
216 else java_call
= "self"
217 java_call
+= ".{java_method_id}({java_args.join(", ")})"
219 if return_type
!= null then java_call
= "return {java_return_type.return_cast}" + java_call
223 if is_static
== true then t
= ""
228 {{{t}}}# Java implementation: {{{java_return_type}}} {{{java_class}}}.{{{java_method_id}}}({{{java_params.join(", ")}}})
229 {{{ct}}}{{{nit_signature.join}}} in "Java" `{
230 {{{ct}}} {{{java_call}}};
235 # Generate getter and setter to access an attribute, of field
236 private fun generate_getter_setter
(java_class
: JavaClass, java_id
: String,
237 attribute
: JavaAttribute)
239 var java_type
= attribute
.java_type
240 var nit_type
= model
.java_to_nit_type
(java_type
)
243 if attribute
.is_static
then nit_id
= java_class
.class_type
.extern_name
.to_snake_case
+ "_" + nit_id
244 nit_id
= nit_id
.to_nit_method_name
245 nit_id
= java_class
.nit_name_for
(nit_id
, [java_type
], false, attribute
.is_static
)
248 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
249 if java_type
.is_vararg
then c
= "#"
252 if attribute
.is_static
then
253 recv
= java_class
.class_type
.package_name
258 if attribute
.is_static
then t
= ""
262 {{{t}}}# Java getter: {{{java_class}}}.{{{java_id}}}
263 {{{ct}}}fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
264 {{{ct}}} return {{{recv}}}.{{{java_id}}};
267 {{{t}}}# Java setter: {{{java_class}}}.{{{java_id}}}
268 {{{ct}}}fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
269 {{{ct}}} {{{recv}}}.{{{java_id}}} = value;
275 # Generate getter and setter to access an attribute, of field
276 private fun generate_constructor
(java_class
: JavaClass, constructor
: JavaConstructor, name
: String)
279 var nit_params_s
= ""
280 var java_params_s
= ""
282 if constructor
.params
.not_empty
then
283 var nit_params
= new Array[String]
284 var java_params
= new Array[String]
286 for java_type
in constructor
.params
do
288 java_params
.add
"{java_type.param_cast}{param_id}"
290 var nit_type
= model
.java_to_nit_type
(java_type
)
291 nit_params
.add
"{param_id}: {nit_type}"
292 param_id
= param_id
.successor
(1)
294 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
295 if java_type
.is_vararg
then c
= "#"
298 nit_params_s
= "(" + nit_params
.join
(", ") + ")"
299 java_params_s
= java_params
.join
(", ")
303 # Java constructor: {{{java_class}}}
304 {{{c}}} new {{{name}}}{{{nit_params_s}}} in "Java" `{
305 {{{c}}} return new {{{java_class.class_type.package_name}}}({{{java_params_s}}});
311 private fun generate_primitive_array
(java_class
: JavaClass, dimensions
: Int)
313 var base_java_type
= java_class
.class_type
314 var java_type
= base_java_type
.clone
315 java_type
.array_dimension
= dimensions
317 var base_nit_type
= model
.java_to_nit_type
(base_java_type
)
318 var nit_type
= model
.java_to_nit_type
(java_type
)
321 # Java primitive array: {{{java_type}}}
322 extern class {{{nit_type}}} in "Java" `{ {{{java_type.extern_equivalent}}} `}
323 super AbstractJavaArray[{{{base_nit_type}}}]
325 # Get a new array of the given `size`
326 new(size: Int) in "Java" `{ return new {{{base_java_type}}}[(int)size]; `}
328 redef fun [](i) in "Java" `{ return self[(int)i]; `}
330 redef fun []=(i, e) in "Java" `{ self[(int)i] = e; `}
332 redef fun length in "Java" `{ return self.length; `}
335 generate_jni_services
(java_type
)
342 # Generate JNI related services
344 # For now, mostly avoid issue #845, but more services could be generated as needed.
345 private fun generate_jni_services
(java_type
: JavaType)
347 var nit_type
= model
.java_to_nit_type
(java_type
)
350 redef fun new_global_ref import sys, Sys.jni_env `{
351 Sys sys = {{{nit_type}}}_sys(self);
352 JNIEnv *env = Sys_jni_env(sys);
353 return (*env)->NewGlobalRef(env, self);
356 redef fun pop_from_local_frame_with_env(jni_env) `{
357 return (*jni_env)->PopLocalFrame(jni_env, self);
364 # List of Nit keywords
366 # These may also be keywords in Java, but there they would be used capitalized.
367 private var nit_keywords
= new HashSet[String].from
(["abort", "abstract", "and", "assert",
368 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
369 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
370 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
371 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while",
374 "class_name", "get_time", "hash", "inspect", "inspect_head", "is_same_type",
375 "is_same_instance", "object_id", "output", "output_class_name", "sys", "to_s",
377 # Pointer or JavaObject methods
380 # Name of methods used at the top-level
382 # Used by `JavaClass::nit_name_for` with static properties.
383 private var top_level_used_names
= new HashSet[String]
388 # Convert the Java method name `self` to the Nit style
390 # * Converts to snake case
391 # * Strips `Get` and `Set`
392 # * Add suffix `=` to setters
393 fun to_nit_method_name
: String
395 var name
= self.to_snake_case
397 # Strip the '_' prefix
398 while name
.has_prefix
("_") do name
= name
.substring
(1, name
.length-1
)
400 # Escape Nit keywords
401 if nit_keywords
.has
(name
) then name
+= "_"
403 # If the name starts by something other than a letter, prefix with `java_`
404 if not name
.chars
.first
.is_letter
then name
= "java_" + name
406 name
= name
.replace
("$", "_")
412 redef class JavaClass
413 # Property names used in this class
414 private var used_names
= new HashSet[String]
416 # Get an available property name for the Java property with `name` and parameters
418 # If `use_parameters_name` then expect that there will be conflicts,
419 # so use the types of `parameters` to build the name.
420 private fun nit_name_for
(name
: String, parameters
: Array[JavaType], use_parameters_name
: Bool, is_static
: Bool): String
422 # Append the name of each parameter
423 if use_parameters_name
then
424 for param
in parameters
do
425 name
+= "_" + param
.id
429 # Set of property names, local or top-level
432 used_names
= sys
.top_level_used_names
433 else used_names
= self.used_names
435 # As a last resort, append numbers to the name
438 while used_names
.has
(name
) do
439 name
= base_name
+ count
.to_s