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
74 for signature
in signatures
do
76 if c
> 0 then nid
+= c
.to_s
79 generate_method
(jclass
, id
, nid
, 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
)
90 generate_constructor
(jclass
, constructor
, name
)
94 for id
, java_type
in jclass
.attributes
do
95 generate_getter_setter
(jclass
, id
, java_type
)
98 file_out
.write
"end\n\n"
101 if stub_for_unknown_types
then
102 for jtype
in model
.unknown_types
do
103 generate_unknown_class_header
(jtype
)
109 # License for the header of the generated Nit module
111 # This file is part of NIT (http://www.nitlanguage.org).
113 # Licensed under the Apache License, Version 2.0 (the "License");
114 # you may not use this file except in compliance with the License.
115 # You may obtain a copy of the License at
117 # http://www.apache.org/licenses/LICENSE-2.0
119 # Unless required by applicable law or agreed to in writing, software
120 # distributed under the License is distributed on an "AS IS" BASIS,
121 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
122 # See the License for the specific language governing permissions and
123 # limitations under the License.
125 # This code has been generated using `jwrapper`
128 private fun generate_class_header
(jtype
: JavaType)
130 var nit_type
= jtype
.to_nit_type
131 file_out
.write
"# Java class: {jtype.to_package_name}\n"
132 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.to_package_name} `\}\n"
133 file_out
.write
"\tsuper JavaObject\n\n"
136 private fun generate_unknown_class_header
(jtype
: JavaType)
138 var nit_type
= jtype
.extern_name
140 file_out
.write
"extern class {nit_type} in \"Java\
" `\{ {jtype.to_package_name} `\}\n"
141 file_out
.write
"\tsuper JavaObject\n\nend\n"
144 private fun generate_method
(java_class
: JavaClass, jmethod_id
, method_id
: String,
145 jreturn_type
: JavaType, jparam_list
: Array[JavaType])
151 var nit_types
= new Array[NitType]
155 for i
in [0..jparam_list
.length
[ do
156 var jparam
= jparam_list
[i
]
157 var nit_type
= jparam
.to_nit_type
159 if not nit_type
.is_complete
then
160 if jparam
.is_wrapped
then
161 java_class
.imports
.add nit_type
.mod
.as(not null)
163 model
.unknown_types
.add jparam
164 if comment_unknown_types
then
167 nit_type
= jparam
.extern_name
174 if not jparam
.is_collection
then cast
= jparam
.param_cast
176 nit_types
.add
(nit_type
)
178 if i
== jparam_list
.length
- 1 then
179 java_params
+= "{cast}{nit_id}{nit_id_no}"
180 nit_params
+= "{nit_id}{nit_id_no}: {nit_type}"
182 java_params
+= "{cast}{nit_id}{nit_id_no}" + ", "
183 nit_params
+= "{nit_id}{nit_id_no}: {nit_type}, "
189 # Method documentation
190 var doc
= "\t# Java implementation: {java_class}.{jmethod_id}\n"
193 method_id
= method_id
.to_nit_method_name
194 method_id
= java_class
.nit_name_for
(method_id
, jparam_list
, java_class
.methods
[jmethod_id
].length
> 1)
195 var nit_signature
= new Array[String]
197 nit_signature
.add
"\tfun {method_id}"
199 if not jparam_list
.is_empty
then
200 nit_signature
.add
"({nit_params})"
203 var return_type
= null
205 if not jreturn_type
.is_void
then
206 return_type
= jreturn_type
.to_nit_type
208 if not return_type
.is_complete
then
209 if jreturn_type
.is_wrapped
then
210 java_class
.imports
.add return_type
.mod
.as(not null)
212 model
.unknown_types
.add jreturn_type
213 if comment_unknown_types
then
216 return_type
= jreturn_type
.extern_name
221 nit_signature
.add
": {return_type} "
225 file_out
.write comment
+ nit_signature
.join
227 if comment
== "#" then
228 file_out
.write
" in \"Java\
" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
229 # Methods with return type
230 else if return_type
!= null then
231 file_out
.write
" in \"Java\
" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n"
232 # Methods without return type
233 else if jreturn_type
.is_void
then
234 file_out
.write
" in \"Java\
" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
237 file_out
.write
" in \"Java\
" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
241 # Generate getter and setter to access an attribute, of field
242 private fun generate_getter_setter
(java_class
: JavaClass, java_id
: String, java_type
: JavaType)
244 var nit_type
= model
.java_to_nit_type
(java_type
)
245 var nit_id
= java_id
.to_nit_method_name
246 nit_id
= java_class
.nit_name_for
(nit_id
, [java_type
], false)
249 if not nit_type
.is_known
then c
= "#"
252 # Java getter: {{{java_class}}}.{{{java_id}}}
253 {{{c}}} fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
254 {{{c}}} return self.{{{java_id}}};
257 # Java setter: {{{java_class}}}.{{{java_id}}}
258 {{{c}}} fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
259 {{{c}}} self.{{{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
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}}});
302 # List of Nit keywords
304 # These may also be keywords in Java, but there they would be used capitalized.
305 private var nit_keywords
: Array[String] = ["abort", "abstract", "and", "assert",
306 "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
307 "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
308 "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
309 "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while"]
314 # Convert the Java method name `self` to the Nit style
316 # * Converts to snake case
317 # * Strips `Get` and `Set`
318 # * Add suffix `=` to setters
319 fun to_nit_method_name
: String
321 var name
= self.to_snake_case
322 if name
.has_prefix
("get_") then
323 name
= name
.substring_from
(4)
324 else if name
.has_prefix
("set_") then
325 name
= name
.substring_from
(4)
326 if nit_keywords
.has
(name
) then name
+= "_"
330 # Strip the '_' prefix
331 while name
.has_prefix
("_") do name
= name
.substring
(1, name
.length-1
)
333 # Escape Nit keywords
334 if nit_keywords
.has
(name
) then name
+= "_"
336 # If the name starts by something other than a letter, prefix with `java_`
337 if not name
.chars
.first
.is_letter
then name
= "java_" + name
339 name
= name
.replace
("$", "_")
345 redef class JavaClass
346 # Property names used in this class
347 private var used_name
= new HashSet[String]
349 # Get an available property name for the Java property with `name` and parameters
351 # If `use_parameters_name` then expect that there will be conflicts,
352 # so use the types of `parameters` to build the name.
353 private fun nit_name_for
(name
: String, parameters
: Array[JavaType], use_parameters_name
: Bool): String
355 # Append the name of each parameter
356 if use_parameters_name
then
357 for param
in parameters
do
358 name
+= "_" + param
.id
362 # As a last resort, append numbers to the name
365 while used_name
.has
(name
) do
366 name
= base_name
+ count
.to_s