Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Calling Java methods with a Time argument loses microseconds #3081

Closed
frsyuki opened this issue Jun 24, 2015 · 2 comments
Closed

Calling Java methods with a Time argument loses microseconds #3081

frsyuki opened this issue Jun 24, 2015 · 2 comments

Comments

@frsyuki
Copy link
Contributor

frsyuki commented Jun 24, 2015

When I have this method in Java,

public void set(Object v) {
  System.out.println("java: "+v.getClass());
}

calling this method using following JRuby code shows java: class java.util.Date:

set(Time.now)

On the other hand, following java method won't be called:

public set(RubyTime v) {
  System.out.println("this method is not called because JRuby's Time won't be coerced to org.jruby.RubyTime");
}

Ruby's Time object represents seconds and nanoseconds. However, java.util.Date represents only milliseconds. So, with the first natural ruby code, microsecond and nanosecond information is lost.

I want to avoid this behavior when I implement a library in Java which deals with timestamp objects. An options is to require users of my library to use java_method(:set, [org.jruby.RubyTime]) but I also want to avoid it because user's code becomes verbose.

Is it possible to change this behavior so that set(Time.now) calls set(RubyTime v) if this Java class has this overload?

@kares
Copy link
Member

kares commented Jun 24, 2015

sounds like you're designing a native JRuby extention part maybe? ... in that case you're probably be able to handle this on you own (@JRubyMethod public set(IRubyObject v)).

the coercion needs to happen if there's a set(Object v) version, I'm not sure if the RubyTime version would work if you remove the Object version so that its the only one ...

@frsyuki
Copy link
Contributor Author

frsyuki commented Jun 25, 2015

Thank you for your comment. I'm creating a Java library which works both for Java and JRuby.

I could solve this! Apparently, @JRubyMethod annotation is ignored if the class does not extend RubyObject? So alternatively, I used this approach:

First, I added setRubyObject(IRubyObject) method to the java class:

public class DynamicColumnSetter
{
    public void setNull() { ... }

    public void set(boolean value) { ... }

    public void set(long value) { ... }

    public void set(double value) { ... }

    public void set(String value) { ... }

    public void set(Instant value) { ... }

    public IRubyObject setRubyObject(IRubyObject rubyObject)
    {
        if (rubyObject instanceof RubyNil) {
            setNull();
        } else if (rubyObject instanceof RubyBoolean) {
            RubyBoolean b = (RubyBoolean) rubyObject;
            set(b.isTrue());
        } else if (rubyObject instanceof RubyInteger) {
            RubyInteger i = (RubyInteger) rubyObject;
            set(i.getLongValue());
        } else if (rubyObject instanceof RubyFloat) {
            RubyFloat f = (RubyFloat) rubyObject;
            set(f.getDoubleValue());
        } else if (rubyObject instanceof RubyString) {
            RubyString s = (RubyString) rubyObject;
            set(s.asJavaString());
        } else if (rubyObject instanceof RubyTime) {
            RubyTime time = (RubyTime) rubyObject;
            long msec = time.getDateTime().getMillis();
            long nsec = time.getNSec();
            long sec = msec / 1000 + nsec / 1000000000;
            int nano = (int) ((msec % 1000) * 1000000 + nsec % 1000000000);
            set(Instant.ofEpochSecond(sec, nano));
        } else {
            throw rubyObject.getRuntime().newTypeError("cannot convert instance of " + rubyObject.getMetaClass() + " to nil, true, false, Integer, Float, String, or Time");
        }
        return rubyObject.getRuntime().getNil();
    }
}

And added following lines to Ruby code:

org.embulk.spi.util.dynamic.DynamicColumnSetter.module_eval do
  alias_method(:set, :setRubyObject)
end

With this way, both Java and Ruby code can call set method simply. But internally, set method in Ruby is translated to setRubyObject method.

@frsyuki frsyuki closed this as completed Jun 25, 2015
@kares kares added this to the Invalid or Duplicate milestone Jun 25, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants