Merge: jwrapper generates full APIs, with contructors, getters and setters
authorJean Privat <jean@pryen.org>
Sat, 25 Jul 2015 23:55:12 +0000 (19:55 -0400)
committerJean Privat <jean@pryen.org>
Sat, 25 Jul 2015 23:55:12 +0000 (19:55 -0400)
This PR is the second part to #1578, it updates the structure of jwrapper, adds a lot of features and creates usable APIs.

Main changes:
* Rewrite of the AST visitor, type conversion and entity naming.
* Generate constructors.
* Generate getter/setters to attributes.
* Add the -p option to choose the prefix of extern class names, use the namespace by default.
* Add the -i option to scan for existing wrappers in any directory or file.
* Add the -r option to filter the target classes from a Jar archive using a regular expression.
* Add more simple tests with javap outputs.
* Add a complete (and working) example of using a custom Java collection from Nit.
* Add an example of generating an API for Android using jwrapper. This API passes nitpick, but it has not been tested as it needs to be integrated in our Android framework.

What's to do next:
* Support for primitive arrays. (This is the main reason why ~10% of the functions are disabled)
* Add static functions as top-level methods.
* Generate class hierarchy.
* Use nitls with the -i option and to add importations to the generated module.
* Fix generic params usage.

Pull-Request: #1589
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Romain Chanoir <chanoir.romain@courrier.uqam.ca>

20 files changed:
contrib/jwrapper/Makefile
contrib/jwrapper/examples/android_api/.gitignore [new file with mode: 0644]
contrib/jwrapper/examples/android_api/Makefile [new file with mode: 0644]
contrib/jwrapper/examples/queue/.gitignore [new file with mode: 0644]
contrib/jwrapper/examples/queue/Makefile [new file with mode: 0644]
contrib/jwrapper/examples/queue/Queue.java [new file with mode: 0644]
contrib/jwrapper/examples/queue/user_test.nit [new file with mode: 0644]
contrib/jwrapper/examples/queue/user_test.sav [new file with mode: 0644]
contrib/jwrapper/grammar/javap.sablecc
contrib/jwrapper/src/code_generator.nit
contrib/jwrapper/src/javap_visitor.nit
contrib/jwrapper/src/jtype_converter.nit
contrib/jwrapper/src/jwrapper.nit
contrib/jwrapper/src/model.nit
contrib/jwrapper/tests/inits.javap [new file with mode: 0644]
contrib/jwrapper/tests/long.javap [new file with mode: 0644]
contrib/jwrapper/tests/many.javap [new file with mode: 0644]
contrib/jwrapper/tests/testjvm.javap [new file with mode: 0644]
lib/java/java.nit
src/ffi/java.nit

index 4bdd6a6..4a52abf 100644 (file)
@@ -20,12 +20,41 @@ clean:
 
 check: bin/jwrapper tests/wildcards.javap
        mkdir -p tmp
+       bin/jwrapper -v -u comment -o tests/long.nit tests/long.javap
+       ../../bin/nitpick -q tests/long.nit
+       bin/jwrapper -v -u comment -o tests/inits.nit tests/inits.javap
+       ../../bin/nitpick -q tests/inits.nit
+       bin/jwrapper -v -u comment -o tests/testjvm.nit tests/testjvm.javap
+       ../../bin/nitpick -q tests/testjvm.nit
+       bin/jwrapper -v -u comment -o tests/many.nit tests/many.javap
+       ../../bin/nitpick -q tests/many.nit
        bin/jwrapper -v -u comment -o tests/wildcards.nit tests/wildcards.javap
        ../../bin/nitpick -q tests/wildcards.nit
+       make -C examples/queue/ check
 
 check-libs: bin/jwrapper
        # This config dependent rule must be tweaked according to each system
-       bin/jwrapper -v -u ignore -o tests/rt.nit /usr/lib/jvm/default-java/jre/lib/rt.jar
-       bin/jwrapper -v -u ignore -o tests/java_tools.nit /usr/lib/jvm/default-java/lib/tools.jar
-       bin/jwrapper -v -u ignore -o tests/sablecc.nit ~/apps/sablecc-3-beta.3.altgen.20041114/lib/sablecc.jar
-       bin/jwrapper -v -u ignore -o tests/android.nit ~/sdks/android-sdk/platforms/android-10/android.jar
+
+       # The full local Java standard library
+       bin/jwrapper -v -u comment -o tests/rt_full.nit /usr/lib/jvm/default-java/jre/lib/rt.jar
+       echo "+ Disabled functions: `grep '#    fun' tests/rt_full.nit | wc -l` / `grep '^      fun' tests/rt_full.nit | wc -l`"
+       nitpick tests/rt_full.nit
+
+       # Only the `java` namespace of the standard library to avoid conflicts with other libs
+       bin/jwrapper -v -u comment -o tests/rt.nit /usr/lib/jvm/default-java/jre/lib/rt.jar -r ^java
+       echo "+ Disabled functions: `grep '#    fun' tests/rt.nit | wc -l` / `grep '^   fun' tests/rt.nit | wc -l`"
+       nitpick tests/rt.nit
+
+       # tools.jar, not using the standard library because of conflicts on sun.tools.jar.*
+       bin/jwrapper -v -u comment -o tests/java_tools.nit /usr/lib/jvm/default-java/lib/tools.jar -i tests/rt.nit
+       sed -i -e "s/import java/import java\nimport rt/" tests/java_tools.nit
+       echo "+ Disabled functions: `grep '#    fun' tests/java_tools.nit | wc -l` / `grep '^   fun' tests/java_tools.nit | wc -l`"
+       nitpick tests/java_tools.nit
+
+       # SableCC using the standard Java library
+       bin/jwrapper -v -u comment -o tests/sablecc.nit ~/apps/sablecc-3-beta.3.altgen.20041114/lib/sablecc.jar -i tests/rt.nit
+       sed -i -e "s/import java/import java\nimport rt/" tests/sablecc.nit
+       echo "+ Disabled functions: `grep '#    fun' tests/sablecc.nit | wc -l` / `grep '^      fun' tests/sablecc.nit | wc -l`"
+       nitpick tests/sablecc.nit
+
+       make -C examples/android_api/ check
diff --git a/contrib/jwrapper/examples/android_api/.gitignore b/contrib/jwrapper/examples/android_api/.gitignore
new file mode 100644 (file)
index 0000000..6e83ff1
--- /dev/null
@@ -0,0 +1,3 @@
+android.nit
+std.nit
+tmp/
diff --git a/contrib/jwrapper/examples/android_api/Makefile b/contrib/jwrapper/examples/android_api/Makefile
new file mode 100644 (file)
index 0000000..e26ba9c
--- /dev/null
@@ -0,0 +1,19 @@
+ANDROID_JAR ?= ~/sdks/android-sdk/platforms/android-10/android.jar
+
+all: android_api.nit
+
+java_api.nit:
+       mkdir -p tmp
+       ../../bin/jwrapper -v -u comment -o java_api.nit -r "^java" $(ANDROID_JAR)
+
+android_api.nit: java_api.nit
+       ../../bin/jwrapper -v -u comment -o android_api.nit -r "^android" -i java_api.nit $(ANDROID_JAR)
+       echo "+ Disabled functions: `grep '#    fun' $@ | wc -l` / `grep '^     fun' $@ | wc -l`"
+
+       # Insert an import between the 2 modules
+       sed -i -e "s/import java/import java\nimport java_api/" android_api.nit
+
+check: android_api.nit
+       ../../../../bin/nitpick android_api.nit
+
+.PHONY: android_api.nit java_api.nit
diff --git a/contrib/jwrapper/examples/queue/.gitignore b/contrib/jwrapper/examples/queue/.gitignore
new file mode 100644 (file)
index 0000000..5343079
--- /dev/null
@@ -0,0 +1,5 @@
+Queue.class
+queue.nit
+user_test
+user_test.res
+user_test.jar
diff --git a/contrib/jwrapper/examples/queue/Makefile b/contrib/jwrapper/examples/queue/Makefile
new file mode 100644 (file)
index 0000000..a07438c
--- /dev/null
@@ -0,0 +1,22 @@
+# Nit test program
+user_test: queue.nit $(shell ../../../../bin/nitls -M user_test.nit) ../../../../bin/nitc
+       CLASSPATH=`pwd` ../../../../bin/nitc user_test.nit
+
+       # Manually add our class file to the Jar for easy access
+       jar -uf user_test.jar Queue.class
+
+# Compiled Java class
+Queue.class: Queue.java
+       javac Queue.java
+
+# The Nit wrapper to the Java class
+queue.nit: Queue.class
+       ../../bin/jwrapper Queue.class -o queue.nit -p "Java" -i auto
+
+# Test
+check: user_test
+       # Execute test
+       ./user_test > user_test.res
+
+       # Compare the result with the expected
+       diff user_test.sav user_test.res
diff --git a/contrib/jwrapper/examples/queue/Queue.java b/contrib/jwrapper/examples/queue/Queue.java
new file mode 100644 (file)
index 0000000..daf0709
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+* This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+*/
+
+import java.util.*;
+
+public class Queue
+{
+    // function pointer
+    public native void printError( String errorMsg );
+
+    // internal list
+    private LinkedList<String> list;
+
+    public Queue()
+    {
+        list = new LinkedList<String>();
+    }
+
+    public void push( String element )
+    {
+        System.out.print( "From java, pushing " );
+        System.out.print( element );
+        System.out.print( "\n" );
+        list.addLast( element );
+    }
+
+    public String pop() // knows where is native printError
+    {
+        String element;
+
+        try
+        {
+            element = list.removeFirst();
+        }
+        catch ( NoSuchElementException e )
+        {
+            printError( "From java, empty queue." );
+            element = null;
+            throw e;
+        }
+
+        System.out.print( "From java, popping " );
+        System.out.print( element );
+        System.out.print( "\n" );
+
+        return element;
+    }
+}
diff --git a/contrib/jwrapper/examples/queue/user_test.nit b/contrib/jwrapper/examples/queue/user_test.nit
new file mode 100644 (file)
index 0000000..882a4d4
--- /dev/null
@@ -0,0 +1,23 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import queue
+
+var queue = new JavaQueue
+queue.push "one".to_java_string
+queue.push "two".to_java_string
+queue.push "tree".to_java_string
+print queue.pop
+print queue.pop
+print queue.pop
diff --git a/contrib/jwrapper/examples/queue/user_test.sav b/contrib/jwrapper/examples/queue/user_test.sav
new file mode 100644 (file)
index 0000000..3db08d4
--- /dev/null
@@ -0,0 +1,9 @@
+From java, pushing one
+From java, pushing two
+From java, pushing tree
+From java, popping one
+one
+From java, popping two
+two
+From java, popping tree
+tree
index 34785d5..dc46557 100644 (file)
@@ -9,6 +9,7 @@ separator = ('.'|'/');
 brackets = '[]';
 wildcard = '?';
 compiled_from = 'Compiled from "' (Any-'"')* '"';
+dots = '...';
 
 // ---
 Parser
@@ -25,23 +26,23 @@ class_or_interface = 'class'|'interface';
 
 modifier
        = 'public'|'private'|'protected'|'static'|'final'|'native'|'synchronized'|'abstract'|'threadsafe'|'transient'|'volatile'|'strictfp';
-type = primitive_type brackets*;
-primitive_type
-       = 'boolean'|'byte'|'char'|'short'|'int'|'float'|'long'|'double'
-       | type_ref;
 
-type_ref
-       = full_class_name
-       | generic_identifier 'extends' type_bound
-       | wildcard;
+type = base_type brackets*;
+
+base_type
+       = {primitive:} primitive_base_type
+       | {class:} full_class_name
+       | {extends:} generic_identifier 'extends' type_bound
+       | {super:} generic_identifier 'super' type_bound
+       | {wildcard:} wildcard
+       | {void:} 'void';
+
+primitive_base_type = 'boolean'|'byte'|'char'|'short'|'int'|'float'|'long'|'double';
+
 type_bound
        = {tail:} type_bound '&' full_class_name
        | {head:} full_class_name;
 
-generic_param = '<' generic_parameter_list '>';
-generic_parameter_list
-       = {tail:} generic_parameter_list ',' parameter
-       | {head:} parameter;
 generic_identifier
        = full_class_name
        | wildcard;
@@ -49,22 +50,19 @@ generic_identifier
 full_class_name
        = {tail:} full_class_name separator class_name
        | {head:} class_name;
-class_name = identifier generic_param?;
+class_name = identifier generic_parameters?;
 
-parameter
-       = type '...'?
-       | {wildcard:} wildcard 'super' full_class_name ;
-parameter_list
-       = {tail:} parameter_list ',' parameter
-       | {head:} parameter;
+generic_parameters = '<' parameters '>';
 
-attribute_id = identifier brackets*;
-method_id = identifier;
+parameter = type dots?;
+parameters
+       = {tail:} parameters ',' parameter
+       | {head:} parameter;
 
 property_declaration
-       = {method:} modifier* generic_param? type method_id '(' parameter_list? ')' throws_declaration? ';'
-       | {constructor:} modifier* generic_param? full_class_name '(' parameter_list? ')' throws_declaration? ';'
-       | {attribute:} modifier* type attribute_id throws_declaration? ';'
+       = {method:} modifier* generic_parameters? type identifier '(' parameters? ')' throws_declaration? ';'
+       | {constructor:} modifier* generic_parameters? full_class_name '(' parameters? ')' throws_declaration? ';'
+       | {attribute:} modifier* type identifier brackets* throws_declaration? ';'
        | {static:} modifier* '{' '}' ';'
        | ';';
 
index dcaba43..5e3d770 100644 (file)
@@ -58,33 +58,44 @@ class CodeGenerator
 
                # All importations
                var imports = new HashSet[String]
-               imports.add "import mnit_android\n"
-               for jclass in model.classes do
+               imports.add "import java\n"
+               for key, jclass in model.classes do
                        for import_ in jclass.imports do imports.add "import android::{import_}\n"
                end
                file_out.write imports.join("\n")
+               file_out.write "\n"
 
-               for jclass in model.classes do
+               for key, jclass in model.classes do
 
-                       file_out.write gen_class_header(jclass.class_type)
+                       generate_class_header(jclass.class_type)
 
                        for id, signatures in jclass.methods do
-                               var c = 0
                                for signature in signatures do
-                                       var nid = id
-                                       if c > 0 then nid += c.to_s
-                                       c += 1
-
-                                       file_out.write gen_method(jclass, id, nid, signature.return_type, signature.params)
+                                       generate_method(jclass, id, id, signature.return_type, signature.params)
                                        file_out.write "\n"
                                end
                        end
+
+                       # Constructors
+                       for constructor in jclass.constructors do
+                               var complex = jclass.constructors.length != 1 and constructor.params.not_empty
+                               var base_name = if complex then "from" else ""
+                               var name = jclass.nit_name_for(base_name, constructor.params, complex)
+
+                               generate_constructor(jclass, constructor, name)
+                       end
+
+                       # Attributes
+                       for id, java_type in jclass.attributes do
+                               generate_getter_setter(jclass, id, java_type)
+                       end
+
                        file_out.write "end\n\n"
                end
 
                if stub_for_unknown_types then
-                       for jtype in model.unknown_types do
-                               file_out.write gen_unknown_class_header(jtype)
+                       for jtype, nit_type in model.unknown_types do
+                               generate_unknown_class_header(jtype)
                                file_out.write "\n"
                        end
                end
@@ -109,34 +120,24 @@ class CodeGenerator
 # This code has been generated using `jwrapper`
 """ is writable
 
-       fun gen_class_header(jtype: JavaType): String
+       private fun generate_class_header(jtype: JavaType)
        do
-               var temp = new Array[String]
-               var nit_type = jtype.to_nit_type
-               temp.add "# Java class: {jtype.to_package_name}\n"
-               temp.add "extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n"
-               temp.add "\tsuper JavaObject\n\n"
-
-               return temp.join
+               var nit_type = model.java_to_nit_type(jtype)
+               file_out.write "# Java class: {jtype.to_package_name}\n"
+               file_out.write "extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n"
+               file_out.write "\tsuper JavaObject\n\n"
        end
 
-       fun gen_unknown_class_header(jtype: JavaType): String
+       private fun generate_unknown_class_header(jtype: JavaType)
        do
-               var nit_type: NitType
-               if jtype.extern_name.has_generic_params then
-                       nit_type = jtype.extern_name.generic_params.first
-               else
-                       nit_type = jtype.extern_name
-               end
-
-               var temp = new Array[String]
-               temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n")
-               temp.add("\tsuper JavaObject\n\nend\n")
+               var nit_type = jtype.extern_name
 
-               return temp.join
+               file_out.write "extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n"
+               file_out.write "\tsuper JavaObject\n\nend\n"
        end
 
-       fun gen_method(java_class: JavaClass, jmethod_id, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String
+       private fun generate_method(java_class: JavaClass, jmethod_id, method_id: String,
+               jreturn_type: JavaType, jparam_list: Array[JavaType])
        do
                var java_params = ""
                var nit_params  = ""
@@ -148,27 +149,14 @@ class CodeGenerator
                # Parameters
                for i in [0..jparam_list.length[ do
                        var jparam = jparam_list[i]
-                       var nit_type = jparam.to_nit_type
-
-                       if not nit_type.is_complete then
-                               if jparam.is_wrapped then
-                                       java_class.imports.add nit_type.mod.as(not null)
-                               else
-                                       model.unknown_types.add jparam
-                                       if comment_unknown_types then
-                                               comment = "#"
-                                       else
-                                               nit_type = jparam.extern_name
-                                       end
-                               end
-                       end
+                       var nit_type = model.java_to_nit_type(jparam)
 
-                       var cast = ""
+                       if not nit_type.is_known and comment_unknown_types then comment = "#"
+                       if jparam.is_primitive_array then comment = "#"
 
-                       if not jparam.is_collection then cast = jparam.param_cast
+                       var cast = jparam.param_cast
 
                        nit_types.add(nit_type)
-                       nit_type.arg_id = "{nit_id}{nit_id_no}"
 
                        if i == jparam_list.length - 1 then
                                java_params += "{cast}{nit_id}{nit_id_no}"
@@ -185,7 +173,8 @@ class CodeGenerator
                var doc = "\t# Java implementation: {java_class}.{jmethod_id}\n"
 
                # Method identifier
-               var method_id = nmethod_id.to_nit_method_name
+               method_id = method_id.to_nit_method_name
+               method_id = java_class.nit_name_for(method_id, jparam_list, java_class.methods[jmethod_id].length > 1)
                var nit_signature = new Array[String]
 
                nit_signature.add "\tfun {method_id}"
@@ -195,45 +184,91 @@ class CodeGenerator
                end
 
                var return_type = null
-
                if not jreturn_type.is_void then
-                       return_type = jreturn_type.to_nit_type
-
-                       if not return_type.is_complete then
-                               if jreturn_type.is_wrapped then
-                                       java_class.imports.add return_type.mod.as(not null)
-                               else
-                                       model.unknown_types.add jreturn_type
-                                       if comment_unknown_types then
-                                               comment = "#"
-                                       else
-                                               return_type = jreturn_type.extern_name
-                                       end
-                               end
-                       end
+                       return_type = model.java_to_nit_type(jreturn_type)
+
+                       if not return_type.is_known and comment_unknown_types then comment = "#"
+                       if jreturn_type.is_primitive_array then comment = "#"
 
                        nit_signature.add ": {return_type} "
                end
 
-               var temp = new Array[String]
-
-               temp.add doc
-               temp.add(comment + nit_signature.join)
+               file_out.write doc
+               file_out.write comment + nit_signature.join
 
                if comment == "#" then
-                       temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
+                       file_out.write " in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
                # Methods with return type
                else if return_type != null then
-                       temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n")
+                       file_out.write " in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}self.{jmethod_id}({java_params});\n{comment}\t`\}\n"
                # Methods without return type
                else if jreturn_type.is_void then
-                       temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
+                       file_out.write " in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
                # No copy
                else
-                       temp.add(" in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n")
+                       file_out.write " in \"Java\" `\{\n{comment}\t\tself.{jmethod_id}({java_params});\n{comment}\t`\}\n"
+               end
+       end
+
+       # Generate getter and setter to access an attribute, of field
+       private fun generate_getter_setter(java_class: JavaClass, java_id: String, java_type: JavaType)
+       do
+               var nit_type = model.java_to_nit_type(java_type)
+               var nit_id = java_id.to_nit_method_name
+               nit_id = java_class.nit_name_for(nit_id, [java_type], false)
+
+               var c = ""
+               if not nit_type.is_known and comment_unknown_types then c = "#"
+               if java_type.is_primitive_array then c = "#"
+
+               file_out.write """
+       # Java getter: {{{java_class}}}.{{{java_id}}}
+{{{c}}}        fun {{{nit_id}}}: {{{nit_type}}} in "Java" `{
+{{{c}}}                return self.{{{java_id}}};
+{{{c}}}        `}
+
+       # Java setter: {{{java_class}}}.{{{java_id}}}
+{{{c}}}        fun {{{nit_id}}}=(value: {{{nit_type}}}) in "Java" `{
+{{{c}}}                self.{{{java_id}}} = value;
+{{{c}}}        `}
+
+"""
+       end
+
+       # Generate getter and setter to access an attribute, of field
+       private fun generate_constructor(java_class: JavaClass, constructor: JavaConstructor, name: String)
+       do
+               var c = ""
+               var nit_params_s = ""
+               var java_params_s = ""
+
+               if constructor.params.not_empty then
+                       var nit_params = new Array[String]
+                       var java_params = new Array[String]
+                       var param_id = 'a'
+                       for java_type in constructor.params do
+
+                               java_params.add "{java_type.param_cast}{param_id}"
+
+                               var nit_type = model.java_to_nit_type(java_type)
+                               nit_params.add  "{param_id}: {nit_type}"
+                               param_id = param_id.successor(1)
+
+                               if not nit_type.is_known and comment_unknown_types then c = "#"
+                               if java_type.is_primitive_array then c = "#"
+                       end
+
+                       nit_params_s = "(" + nit_params.join(", ") + ")"
+                       java_params_s = java_params.join(", ")
                end
 
-               return temp.join
+               file_out.write """
+       # Java constructor: {{{java_class}}}
+{{{c}}}        new {{{name}}}{{{nit_params_s}}} in "Java" `{
+{{{c}}}                return new {{{java_class}}}({{{java_params_s}}});
+{{{c}}}        `}
+
+"""
        end
 end
 
@@ -241,11 +276,17 @@ redef class Sys
        # List of Nit keywords
        #
        # These may also be keywords in Java, but there they would be used capitalized.
-       private var nit_keywords: Array[String] = ["abort", "abstract", "and", "assert",
-               "break", "class", "continue", "do", "else", "end", "enum", "extern", "implies",
-               "import", "init", "interface", "intrude", "if", "in", "is", "isa", "for", "label",
+       private var nit_keywords = new HashSet[String].from(["abort", "abstract", "and", "assert",
+               "break", "class", "continue", "do", "else", "end", "enum", "extern", "false", "implies",
+               "import", "init", "interface", "intrude", "if", "in", "is", "isa", "isset", "for", "label",
                "loop", "module", "new", "not", "null", "nullable", "or", "package", "private",
-               "protected", "public", "return", "self", "super", "then", "type", "var", "while"]
+               "protected", "public", "return", "self", "super", "then", "true", "type", "var", "while",
+
+       # Top-level methods
+               "class_name", "get_time", "hash", "is_same_type", "is_same_instance", "output",
+
+       # Pointer or JavaObject methods
+               "free"])
 end
 
 redef class String
@@ -258,13 +299,6 @@ redef class String
        fun to_nit_method_name: String
        do
                var name = self.to_snake_case
-               if name.has_prefix("get_") then
-                       name = name.substring_from(4)
-               else if name.has_prefix("set_") then
-                       name = name.substring_from(4)
-                       if nit_keywords.has(name) then name += "_"
-                       name += "="
-               end
 
                # Strip the '_' prefix
                while name.has_prefix("_") do name = name.substring(1, name.length-1)
@@ -280,3 +314,33 @@ redef class String
                return name
        end
 end
+
+redef class JavaClass
+       # Property names used in this class
+       private var used_name = new HashSet[String]
+
+       # Get an available property name for the Java property with `name` and parameters
+       #
+       # If `use_parameters_name` then expect that there will be conflicts,
+       # so use the types of `parameters` to build the name.
+       private fun nit_name_for(name: String, parameters: Array[JavaType], use_parameters_name: Bool): String
+       do
+               # Append the name of each parameter
+               if use_parameters_name then
+                       for param in parameters do
+                               name += "_" + param.id
+                       end
+               end
+
+               # As a last resort, append numbers to the name
+               var base_name = name
+               var count = 1
+               while used_name.has(name) do
+                       name = base_name + count.to_s
+                       count += 1
+               end
+
+               used_name.add name
+               return name
+       end
+end
index 02c8ee1..c3e5f98 100644 (file)
@@ -28,212 +28,32 @@ intrude import model
 class JavaVisitor
        super Visitor
 
-       var converter: JavaTypeConverter
-
        # Model of all the analyzed classes
        var model: JavaModel
 
+       # Java class in construction
        var java_class: JavaClass is noinit
 
-       var declaration_type: nullable String =  null
-       var declaration_element: nullable String = null
-       var class_type: JavaType is noinit
-
-       var variable_id = ""
-       var variable_type = new JavaType(self.converter) is lazy
-
-       var is_generic_param = false
-       var is_generic_id = false
-       var generic_id = ""
-       var gen_params_index = 0
-
-       # Used to resolve generic return types (T -> foo.faz.Bar)
-       var generic_map = new HashMap[String, Array[String]]
-
-       var is_primitive_array = false
-
-       var method_id = ""
-       var method_return_type = new JavaType(self.converter) is lazy
-       var method_params = new Array[JavaType]
-       var param_index = 0
-
        redef fun visit(n) do n.accept_visitor(self)
-
-       # Add the identifier from `token` to the current context
-       fun add_identifier(token: String)
-       do
-               if declaration_type == "variable" then
-                       if declaration_element == "type" then
-                               if is_generic_param then
-                                       variable_type.generic_params[gen_params_index].identifier.add token
-                               else
-                                       variable_type.identifier.add(token)
-                               end
-                       end
-               else if declaration_type == "method" then
-                       if declaration_element == "return_type" then
-                               if is_generic_param then
-                                       method_return_type.generic_params[gen_params_index].identifier.add token
-                               else
-                                       method_return_type.identifier.add(token)
-                               end
-                       else if declaration_element == "parameter_list" then
-                               if is_generic_param then
-                                       method_params[param_index].generic_params[gen_params_index].identifier.add token
-                               else
-                                       method_params[param_index].identifier.add(token)
-                               end
-                       end
-               end
-       end
 end
 
 redef class Node
-       fun accept_visitor(v: JavaVisitor) do visit_children(v)
-end
-
-redef class Nidentifier
-       redef fun accept_visitor(v)
-       do
-               if v.declaration_type == "class_header" then
-                       # Class declaration
-                       if v.declaration_element == "id" then
-                               v.class_type.identifier.add text
-                               return
-                       end
-
-               else if v.declaration_type == "variable" then
-                       # Attribute declaration
-                       if v.declaration_element == "id" then
-                               v.variable_id += text
-                               return
-                       end
-
-               else if v.declaration_type == "method" then
-
-                       if v.declaration_element == "id" then
-                               # Method id
-                               v.method_id = self.text
-                               return
-                       else if v.declaration_element == "return_type" then
-                               if self.text == "void" then
-                                       # void return type
-                                       v.method_return_type.is_void = true
-                                       return
-                               end
-                       else if v.declaration_element == "parameter_list" then
-                               # Parameters, leave it to add_identifier
-
-                       else if v.is_generic_param then
-                               # Creates a map to resolve generic return types
-                               # Example : public **<T extends android/os/Bundle>** T foo();
-                               if v.is_generic_id then
-                                       v.generic_id = self.text
-                                       v.generic_map[self.text] = new Array[String]
-
-                                       if not v.method_return_type.has_unresolved_types then v.method_return_type.has_unresolved_types = true
-                               else
-                                       v.generic_map[v.generic_id].add text
-                               end
-                       end
-               end
-
-               v.add_identifier text
-       end
+       private fun accept_visitor(v: JavaVisitor) do visit_children(v)
 end
 
-# Primitive array node
-redef class Nbrackets
-       redef fun accept_visitor(v)
-       do
-               if v.declaration_type == "variable" then
-                       if v.declaration_element == "type" then
-                               if v.is_generic_param then
-                                       v.variable_type.generic_params[v.gen_params_index].array_dimension += 1
-                               else
-                                       v.variable_type.array_dimension += 1
-                               end
-                       end
-
-               else if v.declaration_type == "method" then
-
-                       if v.declaration_element == "return_type" then
-                               if v.is_generic_param then
-                                       v.method_return_type.generic_params[v.gen_params_index].array_dimension += 1
-                               else
-                                       v.method_return_type.array_dimension += 1
-                               end
-                       else if v.declaration_element == "parameter_list" then
-                               if v.is_generic_param then
-                                       v.method_params[v.param_index].generic_params[v.gen_params_index].array_dimension += 1
-                               else
-                                       v.method_params[v.param_index].array_dimension += 1
-                               end
-                       end
-
-               end
-
-               super
-       end
-end
-
-redef class N_39dchar_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dboolean_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dfloat_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39ddouble_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dbyte_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dshort_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dint_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class N_39dlong_39d
-       redef fun accept_visitor(v) do v.add_identifier text
-end
-
-redef class Nwildcard
-       # TODO use the lower bound
-       redef fun accept_visitor(v) do v.add_identifier "Object"
-end
-
-#                                  #
-#    C L A S S     H E A D E R     #
-#                                  #
+# ---
+# Class Header
 
 redef class Nclass_declaration
        redef fun accept_visitor(v)
        do
-               v.java_class = new JavaClass
-               v.model.classes.add v.java_class
-               v.class_type = new JavaType(v.converter)
+               var jtype = n_full_class_name.to_java_type
 
-               v.declaration_type = "class_header"
-               v.declaration_element = "id"
-               super
-
-               # Exit class declaration
-               v.declaration_type = null
-               v.declaration_element = null
+               v.java_class = new JavaClass(jtype)
+               v.model.add_class v.java_class
 
-               v.java_class.class_type = v.class_type
+               # Visit all properties
+               super
        end
 end
 
@@ -241,9 +61,7 @@ end
 redef class Nextends_declaration
        redef fun accept_visitor(v)
        do
-               v.declaration_element = "extends"
-               super
-               v.declaration_element = null
+               # TODO
        end
 end
 
@@ -251,33 +69,29 @@ end
 redef class Nimplements_declaration
        redef fun accept_visitor(v)
        do
-               v.declaration_element = "implements"
-               super
-               v.declaration_element = null
+               # TODO
        end
 end
 
-#                                            #
-# P R O P E R T Y    D E C L A R A T I O N S #
-#                                            #
+# ---
+# Properties
 
 # Method declaration
 redef class Nproperty_declaration_method
        redef fun accept_visitor(v)
        do
-               v.declaration_type = "method"
-               v.declaration_element = null
-               super
-               v.declaration_type = null
-
-               if v.method_return_type.has_unresolved_types then v.method_return_type.resolve_types(v.generic_map)
-
-               var method = new JavaMethod(v.method_return_type, v.method_params.clone)
-               v.java_class.methods[v.method_id].add method
-
-               v.method_params.clear
-               v.method_id = ""
-               v.method_return_type = new JavaType(v.converter)
+               var id = n_identifier.text
+               var return_jtype = n_type.to_java_type
+
+               # Collect parameters
+               var n_parameters = n_parameters
+               var params
+               if n_parameters != null then
+                       params = n_parameters.to_a
+               else params = new Array[JavaType]
+
+               var method = new JavaMethod(return_jtype, params)
+               v.java_class.methods[id].add method
        end
 end
 
@@ -285,9 +99,15 @@ end
 redef class Nproperty_declaration_constructor
        redef fun accept_visitor(v)
        do
-               v.declaration_type = "constructor"
-               super
-               v.declaration_type = null
+               # Collect parameters
+               var n_parameters = n_parameters
+               var params
+               if n_parameters != null then
+                       params = n_parameters.to_a
+               else params = new Array[JavaType]
+
+               var method = new JavaConstructor(params)
+               v.java_class.constructors.add method
        end
 end
 
@@ -295,14 +115,14 @@ end
 redef class Nproperty_declaration_attribute
        redef fun accept_visitor(v)
        do
-               v.declaration_type = "variable"
-               super
-               v.declaration_type = null
+               var id = n_identifier.text
+               var jtype = n_type.to_java_type
 
-               v.java_class.attributes[v.variable_id] = v.variable_type
+               # Manually count the array depth as it is after the id
+               var brackets = n_brackets
+               if brackets != null then jtype.array_dimension += brackets.children.length
 
-               v.variable_id = ""
-               v.variable_type = new JavaType(v.converter)
+               v.java_class.attributes[id] = jtype
        end
 end
 
@@ -310,153 +130,130 @@ end
 redef class Nproperty_declaration_static
        redef fun accept_visitor(v)
        do
-               v.declaration_type = "static"
-               super
-               v.declaration_type = null
+               # TODO
        end
 end
 
-# Identifier of a variable
-redef class Nattribute_id
-       redef fun accept_visitor(v)
+# ---
+# Services
+
+redef class Ntype
+       private fun to_java_type: JavaType
        do
-               v.declaration_element = "id"
-               super
-               v.declaration_element = null
+               var jtype = n_base_type.to_java_type
+
+               var brackets = n_brackets
+               if brackets != null then jtype.array_dimension += brackets.children.length
+
+               return jtype
        end
 end
 
-# Identifier of the method
-redef class Nmethod_id
-       redef fun accept_visitor(v)
+redef class Nbase_type
+       private fun to_java_type: JavaType
        do
-               v.declaration_element = "id"
-               super
-               v.declaration_element = null
+               # By default, everything is bound by object
+               # TODO use a more precise bound
+               var jtype = new JavaType
+               jtype.identifier.add_all(["java", "lang", "object"])
+               return jtype
        end
 end
 
-redef class Ntype
-       redef fun accept_visitor(v)
+redef class Nbase_type_class
+       redef fun to_java_type do return n_full_class_name.to_java_type
+end
+
+redef class Nbase_type_primitive
+       redef fun to_java_type
        do
-               if v.declaration_type == "variable" and v.declaration_element != "id" then
-                       v.declaration_element = "type"
-               end
+               # All the concrete nodes under this production are tokens
+               for node in depth do
+                       if not node isa NToken then continue
 
-               if v.declaration_type == "method" and v.declaration_element == null then
-                       # Makes sure it is not the generic return type definition
-                       if not (v.method_return_type.identifier.is_empty and v.is_generic_param) then
-                               v.declaration_element = "return_type"
-                       end
+                       var jtype = new JavaType
+                       jtype.identifier.add node.text
+                       return jtype
                end
 
-               super
+               abort
+       end
+end
 
-               if v.declaration_element == "variable" then
-                       v.declaration_element = null
-               end
+redef class Nbase_type_void
+       redef fun to_java_type
+       do
+               var jtype = new JavaType
+               jtype.is_void = true
+               return jtype
        end
 end
 
-redef class Ngeneric_param
-       redef fun accept_visitor(v)
+redef class Nfull_class_name
+       # All the identifiers composing this class name
+       private fun to_a: Array[String] is abstract
+
+       # Access `n_class_name` on both alternatives
+       private fun n_class_name_common: Nclass_name is abstract
+
+       private fun to_java_type: JavaType
        do
-               # Ignore the weird generic return type declaration
-               if v.declaration_type == "method" then
-                       if v.declaration_element == null then
-                               v.is_generic_param = true
-                       else
-                               v.is_generic_param = true
-                               v.gen_params_index = 0
-
-                               if v.declaration_element == "return_type" then
-                                       v.method_return_type.generic_params = new Array[JavaType]
-                               else if v.declaration_element == "parameter_list" then
-                                       v.method_params[v.param_index].generic_params = new Array[JavaType]
-                               end
-                       end
-               else if v.declaration_type == "variable" then
-                       if v.declaration_element == "type" then
-                               v.is_generic_param = true
-                               v.gen_params_index = 0
-                               v.variable_type.generic_params = new Array[JavaType]
-                       end
-               end
+               var jtype = new JavaType
+               jtype.identifier = to_a
 
-               super
+               # Generic parameters
+               var n_params = n_class_name_common.n_generic_parameters
+               if n_params != null then jtype.generic_params = n_params.n_parameters.to_a
 
-               v.declaration_element = null
-               v.is_generic_param = false
+               return jtype
        end
 end
 
-redef class Ngeneric_identifier
-       redef fun accept_visitor(v)
-       do
-               if v.declaration_type == "method" then
-                       if v.declaration_element == null then
-                               v.is_generic_id = true
-                       end
-               end
+redef class Nfull_class_name_head
+       redef fun n_class_name_common do return n_class_name
 
-               super
+       redef fun to_a do return [n_class_name.n_identifier.text]
+end
 
-               v.is_generic_id = false
+redef class Nfull_class_name_tail
+       redef fun n_class_name_common do return n_class_name
 
+       redef fun to_a
+       do
+               var a = n_full_class_name.to_a
+               a.add n_class_name.n_identifier.text
+               return a
        end
 end
 
-redef class Nparameter_list
-       redef fun accept_visitor(v)
+redef class Nparameters
+       # Get the types composing this list of parameters
+       #
+       # This is used both on methods signatures and type parameters.
+       private fun to_a: Array[JavaType] is abstract
+end
+
+redef class Nparameters_head
+       redef fun to_a do return [n_parameter.to_java_type]
+end
+
+redef class Nparameters_tail
+       redef fun to_a
        do
-               v.declaration_element = "parameter_list"
-               v.param_index = 0
-               super
-               v.declaration_element = null
-               v.param_index = 0
+               var a = n_parameters.to_a
+               a.add n_parameter.to_java_type
+               return a
        end
 end
 
 redef class Nparameter
-       redef fun accept_visitor(v)
+       private fun to_java_type: JavaType
        do
-               if v.declaration_type == "method" then
-                       if v.declaration_element == "parameter_list" then
-                               if v.is_generic_param then
-                                       v.method_params[v.param_index].generic_params.add(new JavaType(v.converter))
-
-                                       super
-
-                                       v.gen_params_index += 1
-                               else
-                                       v.method_params.add(new JavaType(v.converter))
-
-                                       super
+               var jtype = n_type.to_java_type
 
-                                       v.param_index += 1
-                               end
-                       else if v.declaration_element == "return_type" and v.is_generic_param then
+               var dots = n_dots
+               if dots != null then jtype.is_vararg = true
 
-                               v.method_return_type.generic_params.add(new JavaType(v.converter))
-
-                               super
-
-                               v.gen_params_index += 1
-
-                       # For generic return type definition
-                       else if v.declaration_element == null then
-                               super
-                       end
-               else if v.declaration_type == "variable" then
-                       if v.declaration_element == "type" and v.is_generic_param then
-                               v.variable_type.generic_params.add(new JavaType(v.converter))
-
-                               super
-
-                               v.gen_params_index += 1
-                       end
-               else
-                       super
-               end
+               return jtype
        end
 end
index f70d6ed..b574985 100644 (file)
@@ -1,6 +1,7 @@
 # This file is part of NIT (http://www.nitlanguage.org).
 #
 # Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+# Copyright 2015 Alexis Laferrière <alexis.laf@xymus.net>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # Services to convert java type to nit type and get casts if needed
 module jtype_converter
 
+import opts
+
+redef class Sys
+       # Converter between Java and Nit type
+       var converter = new JavaTypeConverter is lazy
+
+       # Option to treat base Java objects as an equivalent to those in Nit?
+       #
+       # This concerns `Byte, Integer, Float, etc`.
+       var opt_cast_objects = new OptionBool("Convert base objects as their primitive equivalent", "-c")
+end
+
 class JavaTypeConverter
 
        var type_map = new HashMap[String, String]
@@ -27,38 +40,39 @@ class JavaTypeConverter
        do
                # Java type to nit type
                type_map["byte"] = "Int"
-               type_map["Byte"] = "Int"
                type_map["short"] = "Int"
-               type_map["Short"] = "Int"
                type_map["int"] = "Int"
-               type_map["Integer"] = "Int"
                type_map["long"] = "Int"
-               type_map["Long"] = "Int"
                type_map["char"] = "Char"
-               type_map["Character"] = "Char"
                type_map["float"] = "Float"
-               type_map["Float"] = "Float"
                type_map["double"] = "Float"
-               type_map["Double"] = "Float"
                type_map["boolean"] = "Bool"
-               type_map["Boolean"] = "Bool"
-               type_map["Object"] = "JavaObject"
-               type_map["String"] = "JavaString"
-               type_map["CharSequence"] = "JavaString"
-
 
                # Cast if the type is given as a parameter
                param_cast_map["byte"] = "(byte)"
-               param_cast_map["Byte"] = "(Byte)"
                param_cast_map["short"] = "(short)"
-               param_cast_map["Short"] = "(short)"
                param_cast_map["float"] = "(float)"
-               param_cast_map["Float"] = "(float)"
                param_cast_map["int"] = "(int)"
-               param_cast_map["Integer"] = "(int)"
 
-               # Cast if the type is given as a return value
-               return_cast_map["CharSequence"] = "(String)"
+               if opt_cast_objects.value then
+                       type_map["Byte"] = "Int"
+                       type_map["Short"] = "Int"
+                       type_map["Integer"] = "Int"
+                       type_map["Long"] = "Int"
+                       type_map["Character"] = "Char"
+                       type_map["Float"] = "Float"
+                       type_map["Double"] = "Float"
+                       type_map["Boolean"] = "Bool"
+                       type_map["CharSequence"] = "JavaString"
+
+                       param_cast_map["Byte"] = "(Byte)"
+                       param_cast_map["Short"] = "(short)"
+                       param_cast_map["Float"] = "(float)"
+                       param_cast_map["Integer"] = "(int)"
+
+                       # Cast if the type is given as a return value
+                       return_cast_map["CharSequence"] = "(String)"
+               end
        end
 
        fun to_nit_type(java_type: String): nullable String
@@ -70,7 +84,7 @@ class JavaTypeConverter
        do
                return self.param_cast_map.get_or_default(java_type, "")
        end
-       
+
        fun cast_as_return(java_type: String): String
        do
                return self.return_cast_map.get_or_default(java_type, "")
index 0d09833..04878da 100644 (file)
@@ -41,9 +41,10 @@ var opts = new OptionContext
 var opt_unknown = new OptionEnum(["comment", "stub", "ignore"], "How to deal with unknown types", 0, "-u")
 var opt_verbose = new OptionCount("Verbosity", "-v")
 var opt_output = new OptionString("Output file", "-o")
+var opt_regex = new OptionString("Regex pattern to filter classes in Jar archives", "-r")
 var opt_help = new OptionBool("Show this help message", "-h", "--help")
 
-opts.add_option(opt_output, opt_unknown, opt_verbose, opt_help)
+opts.add_option(opt_output, opt_unknown, opt_extern_class_prefix, opt_libs, opt_regex, opt_cast_objects, opt_verbose, opt_help)
 opts.parse args
 
 if opts.errors.not_empty or opts.rest.is_empty or opt_help.value then
@@ -65,6 +66,17 @@ if not "javap".program_is_in_path then
        exit 1
 end
 
+var regex = null
+var regex_code = opt_regex.value
+if regex_code != null then
+       regex = regex_code.to_re
+       var error = regex.compile
+       if error != null then
+               print_error "Regex Error: {error}"
+               exit 1
+       end
+end
+
 # List of bytecode Java classes and javap output files
 var class_files = new Array[String]
 var javap_files = new Array[String]
@@ -97,6 +109,10 @@ for input in opts.rest do
                javap.wait
                for path in output.split("\n") do
                        if path.file_extension == "class" then
+
+                               # Filter out the classes that do not answer to the Regex
+                               if regex != null and not path.has(regex) then continue
+
                                class_files.add out_dir / path
                        end
                end
@@ -172,7 +188,7 @@ sys.perfs["core parser"].add clock.lapse
 # Build model
 if opt_verbose.value > 0 then print "# Building model"
 assert root_node isa NStart
-var visitor = new JavaVisitor(converter, model)
+var visitor = new JavaVisitor(model)
 visitor.enter_visit root_node
 sys.perfs["core model"].add clock.lapse
 
index 0f62c4f..8d6da40 100644 (file)
 module model
 
 import more_collections
+import opts
 
 import jtype_converter
 
 class JavaType
-       private var converter: JavaTypeConverter
        var identifier = new Array[String]
        var generic_params: nullable Array[JavaType] = null
+
+       # Is this a void return type?
        var is_void = false
 
+       # Is this type a vararg?
+       var is_vararg = false is writable
+
        # Has some generic type to be resolved (T extends foo => T is resolved to foo)
        var has_unresolved_types = false
 
@@ -40,8 +45,6 @@ class JavaType
        fun full_id: String do return identifier.join(".")
        fun id: String do return identifier.last.replace("$", "")
 
-       init(converter: JavaTypeConverter) do self.converter = converter
-
        fun return_cast: String do return converter.cast_as_return(self.id)
 
        fun param_cast: String
@@ -53,67 +56,33 @@ class JavaType
                return converter.cast_as_param(self.id)
        end
 
-       fun to_nit_type: NitType
-       do
-               var nit_type: NitType
-               var type_id = null
-
-               if not is_primitive_array then
-                       type_id = converter.to_nit_type(self.id)
-               end
-
-               if type_id == null then
-                       nit_type = self.extern_name
-                       nit_type.is_complete = false
-               else
-                       nit_type = new NitType(type_id)
-               end
-
-               if not self.has_generic_params then return nit_type
-
-               nit_type.generic_params = new Array[NitType]
-
-               for param in generic_params do
-                       var nit_param = param.to_nit_type
-
-                       nit_type.generic_params.add(nit_param)
-
-                       if not nit_param.is_complete then nit_type.is_complete = false
-               end
-
-               return nit_type
-       end
-
-       fun is_collection: Bool do return is_primitive_array or collections_list.has(self.id)
-
-       fun is_wrapped: Bool do return find_extern_class != null
-
-       fun extern_name: NitType
+       # Name to give an extern class wrapping this type
+       fun extern_name: String
        do
-               if is_wrapped then return new NitType.with_module(find_extern_class.as(not null).first, find_extern_class.as(not null).second)
-
                var name
-               if is_primitive_array then
-                       # Primitive arrays have a special naming convention
-                       name = "Java" + extern_class_name.join.capitalized + "Array"
+               var prefix = extern_class_prefix
+               if prefix == null then
+                       # Use the namespace, e.g. java.lang.String -> Java_lang_String
+                       assert not identifier.is_empty
+                       if identifier.length == 1 then
+                               name = identifier.last
+                       else
+                               var first = identifier.first
+                               var last = identifier.last
+                               var mid = identifier.subarray(1, identifier.length-2)
+                               name = first.simple_capitalized + "_"
+                               if mid.not_empty then name += mid.join("_") + "_"
+                               name += last
+                       end
                else
-                       name = "Java" + extern_class_name.join
+                       # Use the prefix and the short class name
+                       # e.g. given the prefix Native: java.lang.String -> NativeString
+                       name = prefix + id
                end
 
                name = name.replace("-", "_")
-
-               var nit_type = new NitType(name)
-               nit_type.is_complete = false
-               return nit_type
-       end
-
-       fun to_cast(jtype: String, is_param: Bool): String
-       do
-               if is_param then
-                       return converter.cast_as_param(jtype)
-               end
-
-               return converter.cast_as_return(jtype)
+               name = name.replace("$", "_")
+               return name
        end
 
        redef fun to_s
@@ -152,142 +121,31 @@ class JavaType
                end
        end
 
-       private fun extern_class_name: Array[String]
-       do
-               var class_name = new Array[String]
-               class_name.add(self.id)
-
-               if not self.has_generic_params then return class_name
-
-               class_name.add "Of"
-
-               for param in generic_params do class_name.add_all param.extern_class_name
-
-               return class_name
-       end
-
-       # Search inside `lib/android` directory for already wrapped classes
-       # If found, contains the class identifier and the Nit Module name
-       var find_extern_class: nullable Couple[String, NitModule] is lazy do
-
-               var regex = "extern class [a-zA-Z1-9]\\\+[ ]\\\+in[ ]\\\+\"Java\"[ ]*`\{[ ]*" + self.to_s + "\\\+[ ]*`\}"
-               var nit_dir = "NIT_DIR".environ
-               if nit_dir.is_empty then return null
-
-               var grep = new ProcessReader("grep", "-r", regex, nit_dir/"lib/android/", nit_dir/"lib/java/")
-               var to_eat = ["private", "extern", "class"]
-
-               var output = grep.read_line
-
-               var output_class = output.substring_from(output.index_of(':') + 1)
-               var tokens = output_class.split(" ")
-
-               var nclass_name = ""
-
-               for token in tokens do
-                       if to_eat.has(token) then continue
-                       nclass_name = token
-                       break
-               end
-
-               if nclass_name == "" then return null
-
-               var str = output.substring(0, output.search(".nit").from)
-               str = str.substring_from(str.last_index_of('/') + 1)
-               var mod = new NitModule(str)
-
-               return new Couple[String, NitModule](nclass_name, mod)
-       end
-
-       # Comparison based on fully qualified named and generic params
-       # Ignores primitive array so `a.b.c[][] == a.b.c`
-       redef fun ==(other)
-       do
-               if other isa JavaType then
-                       return self.repr == other.repr
-               end
-               return false
-       end
-
-       redef fun hash do return self.repr.hash
-
-       private fun repr: String
-       do
-               var id = self.full_id
-
-               if self.has_generic_params then
-                       var gen_list = new Array[String]
-
-                       for param in generic_params do
-                               gen_list.add(param.to_s)
-                       end
-
-                       id += "<{gen_list.join(", ")}>"
-               end
+       # Comparison based on fully qualified named
+       redef fun ==(other) do return other isa JavaType and
+               self.full_id == other.full_id and
+               self.is_primitive_array == other.is_primitive_array
 
-               return id
-       end
-
-       var collections_list: Array[String] is lazy do return ["List", "ArrayList", "LinkedList", "Vector", "Set", "SortedSet", "HashSet", "TreeSet", "LinkedHashSet", "Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
-       var iterable: Array[String] is lazy do return ["ArrayList", "Set", "HashSet", "LinkedHashSet", "LinkedList", "Stack", "TreeSet", "Vector"]
-       var maps: Array[String] is lazy do return ["Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
+       redef fun hash do return self.full_id.hash
 end
 
 class NitType
+       # Nit class name
        var identifier: String
-       var arg_id: String
-       var generic_params: nullable Array[NitType] = null
 
        # If this NitType was found in `lib/android`, contains the module name to import
        var mod: nullable NitModule
 
-       # Returns `true` if all types have been successfully converted to Nit type
-       var is_complete: Bool = true
-
-       fun has_generic_params: Bool do return not generic_params == null
-
-       fun id: String do return identifier
-
-       init (id: String)
-       do
-               self.identifier = id
-       end
-
-       init with_generic_params(id: String, gen_params: String...)
-       do
-               self.init(id)
-               self.generic_params = new Array[NitType]
-               for param in gen_params do self.generic_params.add new NitType(param)
-       end
-
-       init with_module(id: String, mod: NitModule)
-       do
-               self.init(id)
-               self.mod = mod
-       end
-
-       redef fun to_s: String
-       do
-               var id = self.identifier
-
-               if self.has_generic_params then
-                       var gen_list = new Array[String]
-
-                       for param in generic_params do
-                               gen_list.add(param.to_s)
-                       end
-
-                       id += "[{gen_list.join(", ")}]"
-               end
+       # Is this type known, wrapped and available in Nit?
+       var is_known: Bool = true
 
-               return id
-       end
+       redef fun to_s do return identifier
 end
 
 # Model of a single Java class
 class JavaClass
        # Type of this class
-       var class_type = new JavaType(new JavaTypeConverter)
+       var class_type: JavaType
 
        # Attributes of this class
        var attributes = new HashMap[String, JavaType]
@@ -295,6 +153,9 @@ class JavaClass
        # Methods of this class organized by their name
        var methods = new MultiHashMap[String, JavaMethod]
 
+       # Constructors of this class
+       var constructors = new Array[JavaConstructor]
+
        # Importations from this class
        var imports = new HashSet[NitModule]
 
@@ -303,11 +164,66 @@ end
 
 # Model of all the Java class analyzed in one run
 class JavaModel
-       # Unknown Java types used in `classes`
-       var unknown_types = new HashSet[JavaType]
 
        # All analyzed classes
-       var classes = new Array[JavaClass]
+       var classes = new HashMap[String, JavaClass]
+
+       # Add a class in `classes`
+       fun add_class(jclass: JavaClass)
+       do
+               var key = jclass.class_type.full_id
+               classes[key] = jclass
+       end
+
+       # Unknown types, not already wrapped and not in this pass
+       private var unknown_types = new HashMap[JavaType, NitType]
+
+       # Wrapped types, or classes analyzed in this pass
+       private var known_types = new HashMap[JavaType, NitType]
+
+       # Get the `NitType` corresponding to the `JavaType`
+       #
+       # Also registers types so they can be reused and
+       # to keep track of unknown types.
+       fun java_to_nit_type(jtype: JavaType): NitType
+       do
+               # Check cache
+               if known_types.keys.has(jtype) then return known_types[jtype]
+               if unknown_types.keys.has(jtype) then return unknown_types[jtype]
+
+               # Is it a compatible primitive type?
+               if not jtype.is_primitive_array then
+                       var name = converter.to_nit_type(jtype.id)
+                       if name != null then
+                               # We got a Nit equivalent
+                               var nit_type = new NitType(name)
+                               known_types[jtype] = nit_type
+                               return nit_type
+                       end
+               end
+
+               # Is being wrapped in this pass?
+               var key = jtype.full_id
+               if classes.keys.has(key) then
+                       var nit_type = new NitType(jtype.extern_name)
+                       known_types[jtype] = nit_type
+
+                       return nit_type
+               end
+
+               # Search in lib
+               var nit_type = find_extern_class[jtype.full_id]
+               if nit_type != null then
+                       known_types[jtype] = nit_type
+                       return nit_type
+               end
+
+               # Unknown type
+               nit_type = new NitType(jtype.extern_name)
+               nit_type.is_known = false
+               unknown_types[jtype] = nit_type
+               return nit_type
+       end
 end
 
 # A Java method, with its signature
@@ -319,12 +235,103 @@ class JavaMethod
        var params: Array[JavaType]
 end
 
+# A Java method, with its signature
+class JavaConstructor
+       # Type of the parameters of this constructor
+       var params: Array[JavaType]
+end
+
 # A Nit module, use to import the referenced extern classes
 class NitModule
+       # Relative path to the module
+       var path: String
+
        # Name of the module
-       var name: String
+       var name: String is lazy do return path.basename(".nit")
+
+       redef fun to_s do return self.name
+       redef fun ==(other) do return other isa NitModule and self.path == other.path
+       redef fun hash do return self.path.hash
+end
 
-       redef fun ==(other): Bool do return self.to_s == other.to_s
-       redef fun to_s: String do return self.name
-       redef fun hash: Int do return self.name.hash
+redef class Sys
+       # Collection of Java classes already wrapped in the library
+       #
+       # * The key is from `JavaType.full_id`.
+       # * The value is the corresponding `NitType`.
+       var find_extern_class: DefaultMap[String, nullable NitType] is lazy do
+               var map = new DefaultMap[String, nullable NitType](null)
+               var modules = new HashMap[String, NitModule]
+
+               var lib_paths = opt_libs.value
+               if lib_paths == null then lib_paths = new Array[String]
+
+               if lib_paths.has("auto") then
+                       lib_paths.remove "auto"
+                       var nit_dir = "NIT_DIR".environ
+                       if nit_dir.is_empty then
+                               # Simple heuristic to find the Nit lib
+                               var dir = sys.program_name.dirname / "../../../lib/"
+                               dir = dir.simplify_path
+                               if dir.file_exists then lib_paths.add dir.simplify_path
+                       end
+               end
+
+               if lib_paths.is_empty then return map
+
+               # Use grep to find all extern classes implemented in Java
+               var grep_regex = "extern class [a-zA-Z0-9_]\\\+[ ]\\\+in[ ]\\\+\"Java\""
+               var grep_args = ["-r", "--with-filename", grep_regex]
+               grep_args.add_all lib_paths
+
+               var grep = new ProcessReader("grep", grep_args...)
+               var lines = grep.read_lines
+               grep.close
+               grep.wait
+
+               # Sort out the modules, Nit class names and Java types
+               var regex = """(.+):\\s*extern +class +([a-zA-Z0-9_]+) *in *"Java" *`\\{ *([a-zA-Z0-9.$/]+) *`\\}""".to_re
+               for line in lines do
+                       var matches = line.search_all(regex)
+                       for match in matches do
+                               var path = match[1].to_s
+                               var nit_name = match[2].to_s
+                               var java_name = match[3].to_s
+
+                               # Debug code
+                               # print "+ Found {nit_name}:{java_name} at {path}"
+
+                               var mod = modules.get_or_null(path)
+                               if mod == null then
+                                       mod = new NitModule(path)
+                                       modules[path] = mod
+                               end
+
+                               map[java_name] = new NitType(nit_name, mod)
+                       end
+               end
+
+               return map
+       end
+
+       # Option to set `extern_class_prefix`
+       var opt_extern_class_prefix = new OptionString("Prefix to extern classes (By default uses the full namespace)", "-p")
+
+       # Prefix used to name extern classes, if `null` use the full namespace
+       var extern_class_prefix: nullable String is lazy do return opt_extern_class_prefix.value
+
+       # Libraries to search for existing wrappers
+       var opt_libs = new OptionArray("Paths to libraries with wrappers of Java classes ('auto' to use the full Nit lib)", "-i")
+end
+
+redef class Text
+       # Get a copy of `self` where the first letter is capitalized
+       fun simple_capitalized: String
+       do
+               if is_empty then return to_s
+
+               var c = chars.first.to_upper
+               var s = c.to_s + substring_from(1)
+               return s
+       end
 end
diff --git a/contrib/jwrapper/tests/inits.javap b/contrib/jwrapper/tests/inits.javap
new file mode 100644 (file)
index 0000000..270934d
--- /dev/null
@@ -0,0 +1,5 @@
+public class org.example.ClassA {
+       public org.example.ClassA();
+       public org.example.ClassA(long);
+       public org.example.ClassA(java.lang.Object);
+}
diff --git a/contrib/jwrapper/tests/long.javap b/contrib/jwrapper/tests/long.javap
new file mode 100644 (file)
index 0000000..5cf849f
--- /dev/null
@@ -0,0 +1,3 @@
+
+class com.sun.xml.internal.bind.v2.model.impl.RuntimeElementInfoImpl$RuntimePropertyImpl extends com.sun.xml.internal.bind.v2.model.impl.ElementInfoImpl<java.lang.reflect.Type, java.lang.Class, java.lang.reflect.Field, java.lang.reflect.Method>.PropertyImpl implements com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementPropertyInfo, com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeRef {
+}
diff --git a/contrib/jwrapper/tests/many.javap b/contrib/jwrapper/tests/many.javap
new file mode 100644 (file)
index 0000000..9e05bcf
--- /dev/null
@@ -0,0 +1,8 @@
+public class org.example.ClassA {
+       public org.example.ClassB Foo(org.example.ClassC);
+       public org.example.ClassB Bar(org.example.ClassC);
+       public org.example.ClassB an_attribute;
+}
+public class org.example.ClassB extends org.example.ClassA {
+       public org.example.ClassA Foo(org.example.ClassC);
+}
diff --git a/contrib/jwrapper/tests/testjvm.javap b/contrib/jwrapper/tests/testjvm.javap
new file mode 100644 (file)
index 0000000..1c08c8c
--- /dev/null
@@ -0,0 +1,9 @@
+Compiled from "TestJvm.java"
+public class test_jvm.TestJvm {
+  public test_jvm.TestJvm();
+  public test_jvm.Test2 getTest();
+  public boolean isBool();
+  public char getC();
+  public int getI();
+  public float getF();
+}
index cef7b22..04e8743 100644 (file)
@@ -104,7 +104,7 @@ extern class JavaString in "Java" `{ java.lang.String `}
                JNIEnv *env = Sys_jni_env(sys);
 
                // Get the data from Java
-               const jbyte *java_cstr = (char*)(*env)->GetStringUTFChars(env, self, NULL);
+               const char *java_cstr = (*env)->GetStringUTFChars(env, self, NULL);
                jsize len = (*env)->GetStringUTFLength(env, self);
 
                // Copy it in control of Nit
index da641a5..dd32712 100644 (file)
@@ -457,7 +457,7 @@ redef class MType
        # JNI type name (in C)
        #
        # So this is a C type, usually defined in `jni.h`
-       private fun jni_type: String do return "jint"
+       private fun jni_type: String do return "long"
 
        # JNI short type name (for signatures)
        #