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} is no_warning(\"useless-superclass\
")\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 # Sort classes from top-level classes (java.lang.Object) to leaves
69 var standard_classes
= new Array[JavaClass]
70 for name
, jclass
in model
.classes
do
71 if not jclass
.class_type
.is_anonymous
then standard_classes
.add jclass
73 var linearized
= model
.class_hierarchy
.linearize
(standard_classes
)
75 for jclass
in linearized
do
76 # Skip classes with an invalid name at the Java language level
77 if jclass
.class_type
.extern_equivalent
.has
("-") then continue
79 generate_class_header
(jclass
)
81 for id
, signatures
in jclass
.local_intro_methods
do
82 for signature
in signatures
do
83 assert not signature
.is_static
84 generate_method
(jclass
, id
, id
, signature
.return_type
, signature
.params
)
90 for constructor
in jclass
.constructors
do
91 var complex
= jclass
.constructors
.length
!= 1 and constructor
.params
.not_empty
92 var base_name
= if complex
then "from" else ""
93 var name
= jclass
.nit_name_for
(base_name
, constructor
.params
, complex
, false, local_only
=true)
95 generate_constructor
(jclass
, constructor
, name
)
99 for id
, attribute
in jclass
.attributes
do if not attribute
.is_static
then
100 generate_getter_setter
(jclass
, id
, attribute
)
104 generate_jni_services jclass
.class_type
107 file_out
.write
"end\n\n"
109 # Static functions as top-level methods
110 var static_functions_prefix
= jclass
.class_type
.extern_name
.to_snake_case
111 for id
, signatures
in jclass
.methods
do
112 for signature
in signatures
do if signature
.is_static
then
113 var nit_id
= static_functions_prefix
+ "_" + id
114 generate_method
(jclass
, id
, nit_id
, signature
.return_type
, signature
.params
, is_static
=true)
119 # Static attributes as top-level getters and setters
120 for id
, attribute
in jclass
.attributes
do if attribute
.is_static
then
121 generate_getter_setter
(jclass
, id
, attribute
)
125 for d
in [1..opt_arrays
.value
] do
126 generate_primitive_array
(jclass
, d
)
130 if stub_for_unknown_types
then
131 for jtype
, nit_type
in model
.unknown_types
do
132 generate_unknown_class_header
(jtype
)
140 # License for the header of the generated Nit module
142 # This file is part of NIT (http://www.nitlanguage.org).
144 # Licensed under the Apache License, Version 2.0 (the "License");
145 # you may not use this file except in compliance with the License.
146 # You may obtain a copy of the License at
148 # http://www.apache.org/licenses/LICENSE-2.0
150 # Unless required by applicable law or agreed to in writing, software
151 # distributed under the License is distributed on an "AS IS" BASIS,
152 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
153 # See the License for the specific language governing permissions and
154 # limitations under the License.
156 # This code has been generated using `jwrapper`
159 private fun generate_class_header
(java_class
: JavaClass)
161 var java_type
= java_class
.class_type
162 var nit_type
= model
.java_to_nit_type
(java_type
)
164 var super_java_types
= new HashSet[JavaType]
165 super_java_types
.add_all java_class
.extends
166 super_java_types
.add_all java_class
.implements
168 var supers
= new Array[String]
169 var effective_supers
= 0
170 for java_super
in super_java_types
do
171 var nit_super
= model
.java_to_nit_type
(java_super
)
173 # Comment out unknown types
175 if not nit_super
.is_known
and comment_unknown_types
then c
= "# "
177 supers
.add
"{c}super {nit_super}"
178 if c
!= "# " then effective_supers
+= 1
181 if effective_supers
== 0 then
182 if java_class
.class_type
.package_name
== "java.lang.Object" then
183 supers
.add
"super JavaObject"
184 else supers
.add
"super Java_lang_Object"
188 # Java class: {{{java_type}}}
189 extern class {{{nit_type}}} in "Java" `{ {{{java_type.extern_equivalent}}} `}
190 {{{supers.join("\n\t")}}}
195 private fun generate_unknown_class_header
(jtype
: JavaType)
197 var nit_type
= jtype
.extern_name
199 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.extern_equivalent} `\}\n"
200 file_out
.write
"\tsuper JavaObject\n\nend\n"
203 private fun generate_method
(java_class
: JavaClass, java_method_id
, method_id
: String,
204 java_return_type
: JavaType, java_params
: Array[JavaType], is_static
: nullable Bool)
206 var java_args
= new Array[String]
207 var nit_params
= new Array[String]
213 for jparam
in java_params
do
214 var nit_type
= model
.java_to_nit_type
(jparam
)
216 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
217 if jparam
.is_vararg
then c
= "#"
219 java_args
.add
"{jparam.param_cast}{nit_id}{nit_id_no}"
220 nit_params
.add
"{nit_id}{nit_id_no}: {nit_type}"
226 method_id
= method_id
.to_nit_method_name
227 method_id
= java_class
.nit_name_for
(method_id
, java_params
, java_class
.methods
[java_method_id
].length
> 1, is_static
== true)
229 # Build the signature
230 var nit_signature
= new Array[String]
231 nit_signature
.add
"fun {method_id}"
232 if not java_params
.is_empty
then nit_signature
.add
"({nit_params.join(", ")})"
235 var return_type
= null
236 if not java_return_type
.is_void
then
237 return_type
= model
.java_to_nit_type
(java_return_type
)
239 if not return_type
.is_known
and comment_unknown_types
then c
= "#"
240 if java_return_type
.is_vararg
then c
= "#"
242 nit_signature
.add
": " + return_type
.to_s
245 # Build the call in Java
247 if is_static
== true then
248 java_call
= java_class
.class_type
.package_name
249 else java_call
= "self"
250 java_call
+= ".{java_method_id}({java_args.join(", ")})"
252 if return_type
!= null then java_call
= "return {java_return_type.return_cast}" + java_call
256 if is_static
== true then t
= ""
261 {{{t}}}# Java implementation: {{{java_return_type}}} {{{java_class}}}.{{{java_method_id}}}({{{java_params.join(", ")}}})
262 {{{ct}}}{{{nit_signature.join}}} in "Java" `{
263 {{{ct}}} {{{java_call}}};
268 # Generate getter and setter to access an attribute, of field
269 private fun generate_getter_setter
(java_class
: JavaClass, java_id
: String,
270 attribute
: JavaAttribute)
272 var java_type
= attribute
.java_type
273 var nit_type
= model
.java_to_nit_type
(java_type
)
276 if attribute
.is_static
then nit_id
= java_class
.class_type
.extern_name
.to_snake_case
+ "_" + nit_id
277 nit_id
= nit_id
.to_nit_method_name
278 nit_id
= java_class
.nit_name_for
(nit_id
, [java_type
], false, attribute
.is_static
)
281 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
282 if java_type
.is_vararg
then c
= "#"
285 if attribute
.is_static
then
286 recv
= java_class
.class_type
.package_name
291 if attribute
.is_static
then t
= ""
295 {{{t}}}# Java getter: {{{java_class}}}.{{{java_id}}}
296 {{{ct}}}fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
297 {{{ct}}} return {{{recv}}}.{{{java_id}}};
300 {{{t}}}# Java setter: {{{java_class}}}.{{{java_id}}}
301 {{{ct}}}fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
302 {{{ct}}} {{{recv}}}.{{{java_id}}} = value;
308 # Generate getter and setter to access an attribute, of field
309 private fun generate_constructor
(java_class
: JavaClass, constructor
: JavaConstructor, name
: String)
312 var nit_params_s
= ""
313 var java_params_s
= ""
315 if constructor
.params
.not_empty
then
316 var nit_params
= new Array[String]
317 var java_params
= new Array[String]
319 for java_type
in constructor
.params
do
321 java_params
.add
"{java_type.param_cast}{param_id}"
323 var nit_type
= model
.java_to_nit_type
(java_type
)
324 nit_params
.add
"{param_id}: {nit_type}"
325 param_id
= param_id
.successor
(1)
327 if not nit_type
.is_known
and comment_unknown_types
then c
= "#"
328 if java_type
.is_vararg
then c
= "#"
331 nit_params_s
= "(" + nit_params
.join
(", ") + ")"
332 java_params_s
= java_params
.join
(", ")
336 # Java constructor: {{{java_class}}}
337 {{{c}}} new {{{name}}}{{{nit_params_s}}} in "Java" `{
338 {{{c}}} return new {{{java_class.class_type.package_name}}}({{{java_params_s}}});
344 private fun generate_primitive_array
(java_class
: JavaClass, dimensions
: Int)
346 var base_java_type
= java_class
.class_type
347 var java_type
= base_java_type
.clone
348 java_type
.array_dimension
= dimensions
350 var base_nit_type
= model
.java_to_nit_type
(base_java_type
)
351 var nit_type
= model
.java_to_nit_type
(java_type
)
354 # Java primitive array: {{{java_type}}}
355 extern class {{{nit_type}}} in "Java" `{ {{{java_type.extern_equivalent}}} `}
356 super AbstractJavaArray[{{{base_nit_type}}}]
358 # Get a new array of the given `size`
359 new(size: Int) in "Java" `{ return new {{{base_java_type}}}[(int)size]; `}
361 redef fun [](i) in "Java" `{ return self[(int)i]; `}
363 redef fun []=(i, e) in "Java" `{ self[(int)i] = e; `}
365 redef fun length in "Java" `{ return self.length; `}
368 generate_jni_services
(java_type
)
375 # Generate JNI related services
377 # For now, mostly avoid issue #845, but more services could be generated as needed.
378 private fun generate_jni_services
(java_type
: JavaType)
380 var nit_type
= model
.java_to_nit_type
(java_type
)
383 redef fun new_global_ref import sys, Sys.jni_env `{
384 Sys sys = {{{nit_type}}}_sys(self);
385 JNIEnv *env = Sys_jni_env(sys);
386 return (*env)->NewGlobalRef(env, self);
389 redef fun pop_from_local_frame_with_env(jni_env) `{
390 return (*jni_env)->PopLocalFrame(jni_env, self);
397 # List of Nit keywords
399 # These may also be keywords in Java, but there they would be used capitalized.
400 private var nit_keywords
= new HashSet[String].from
(["abort", "abstract", "and", "assert",
401 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
402 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
403 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
404 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while",
407 "class_name", "get_time", "hash", "inspect", "inspect_head", "is_same_type",
408 "is_same_instance", "object_id", "output", "output_class_name", "sys", "to_s",
410 # Pointer or JavaObject methods
413 # Name of methods used at the top-level
415 # Used by `JavaClass::nit_name_for` with static properties.
416 private var top_level_used_names
= new HashSet[String]
421 # Convert the Java method name `self` to the Nit style
423 # * Converts to snake case
424 # * Strips `Get` and `Set`
425 # * Add suffix `=` to setters
426 fun to_nit_method_name
: String
428 var name
= self.to_snake_case
430 # Strip the '_' prefix
431 while name
.has_prefix
("_") do name
= name
.substring
(1, name
.length-1
)
433 # Escape Nit keywords
434 if nit_keywords
.has
(name
) then name
+= "_"
436 # If the name starts by something other than a letter, prefix with `java_`
437 if not name
.chars
.first
.is_letter
then name
= "java_" + name
439 name
= name
.replace
("$", "_")
445 redef class JavaClass
446 # Property names used in this class
447 private var used_names
= new HashSet[String]
449 # Get an available property name for the Java property with `name` and parameters
451 # If `use_parameters_name` then expect that there will be conflicts,
452 # so use the types of `parameters` to build the name.
453 private fun nit_name_for
(name
: String, parameters
: Array[JavaType], use_parameters_name
: Bool, is_static
: Bool, local_only
: nullable Bool): String
455 # Append the name of each parameter
456 if use_parameters_name
then
457 for param
in parameters
do
459 id
+= "Array"*param
.array_dimension
464 # Set of sets of property names, local or top-level
469 local_used_names
= sys
.top_level_used_names
470 used_names
= sys
.top_level_used_names
471 else if local_only
== true then
472 # Local only: constructors
473 local_used_names
= self.used_names
474 used_names
= self.used_names
476 # Avoid conflicts with all super classes
477 local_used_names
= self.used_names
478 used_names
= new HashSet[String]
479 for sup
in in_hierarchy
.greaters
do
480 used_names
.add_all sup
.used_names
484 # As a last resort, append numbers to the name
487 while used_names
.has
(name
) do
488 name
= base_name
+ count
.to_s
492 local_used_names
.add name