arton / rjb

Ruby Java Bridge
https://www.artonx.org/collabo/backyard/?RubyJavaBridge
GNU Lesser General Public License v2.1
117 stars 34 forks source link

Inner classes support #100

Closed uvlad7 closed 6 months ago

uvlad7 commented 7 months ago

So, I tried to use okhttp3.FormBody::Builder, but it fails

FormBody = Rjb.import 'okhttp3.FormBody'
FormBody::Builder # #<Rjb::Okhttp3_FormBody:0x000063ae080e6ab8 @user_initialize=nil> is not a class/module (TypeError)
FormBody.Builder # test.rb:10:in `method_missing': Fail: unknown method name `Builder' (RuntimeError)

The only way I found is to import it separately

Builder = Rjb.import 'okhttp3.FormBody$Builder'

after that FormBody.Builder works too. It's not an obvious way and I had to find how Rjb.import works internally (cls = (*jenv)->FindClass(jenv, name);) to find a way to load it. Probably java2jniname should replace "::" with '$', but it still would require separate import. Is it possible to handle this in method_missing? And will it work for non-static inner classes?

arton commented 7 months ago

@uvlad7 Hi, I didn't try to use non-static inner classes. I'll try it, but I know that non-static inner classes require outer (parent) class instance. Therefore you need to import the outer class separately. By the way, your java2jniname enhancement request is very reasonable. I'll change it. Thank you.

uvlad7 commented 7 months ago

non-static inner classes require outer (parent) class instance

Yep, just implicit constructor param, that's it. Probably add this into docs

$ javac -d ./test Test.java && cd test && jar cvf ../test.jar ./* > /dev/null && cd .. && ruby test.rb
"TEST"
"NESTED_TEST"
"INNER_TEST"
"test"
"nested_test"
"inner_test"

Test.java

// javac -d ./test Test.java && cd test && jar cvf ../test.jar ./* && cd ..

public class Test {
    public static String TEST = "TEST";
    public String test;

    public Test(String test) {
        this.test = test;
    }

    public static String static_test() {
        return TEST;
    }

    public String test() {
        return this.test;
    }

    static class StaticNestedTest {
        public static String NESTED_TEST = "NESTED_";
        public String nested_test;

        public StaticNestedTest(String nested_test) {
            this.nested_test = nested_test;
        }

        public static String static_test() {
            return NESTED_TEST + Test.TEST;
        }

        public String test() {
            return this.nested_test /* + Test.this.test */ ;
        }
    }

    class InnerTest {
        public static String INNER_TEST = "INNER_";
        public String inner_test;

        public InnerTest(String inner_test) {
            this.inner_test = inner_test;
        }

        public static String static_test() {
            return INNER_TEST + Test.TEST;
        }

        public String test() {
            return this.inner_test + Test.this.test;
        }
    }
}

test.rb

require 'rjb'
Rjb.load
Rjb.add_jar(File.expand_path('test.jar'))

Test = Rjb.import 'Test'
StaticNestedTest = Rjb.import 'Test$StaticNestedTest'
InnerTest = Rjb.import 'Test$InnerTest'

p Test.static_test
p Test.StaticNestedTest.static_test
p Test.InnerTest.static_test
p Test.new("test").test
p Test.StaticNestedTest.new("nested_test").test
p Test.InnerTest.new(Test.new("test"), "inner_").test
arton commented 6 months ago

@uvlad7 I thought that RJB::import should take Java style parameter, not ruby style, nor JNI style. At this time, it requires '.' for the separator such as 'java.lang.String' not 'java::lang::String' or 'java/lang/String'(but it was accepted) as ruby and JNI do. For inner classes and nested classes (static inner classes), I like to apply the same convention. So I will implement your request with '.' as Java do.

StaticNestedTest = Rjb.import 'Test.StaticNestedTest'
InnerTest = Rjb.import 'Test.InnerTest'

Rjb may change '.' to '$' before the second capital letter token. Your opinion?

uvlad7 commented 6 months ago

Yeah, it would be fine, just harder to implement, I suppose. I believe I've seen a function to convert these signatures somewhere, wasn't able to find it, though

arton commented 6 months ago

Hi @uvlad7, I fixed this issue by rjb-1.7.0.gem. Now you can call with '.'. Thank you.

uvlad7 commented 6 months ago

That's awesome, thank you for your efforts! It's really great to see such a long-lived project!