Oops, I pasted the old sample swing app. I've since modified the signatures to be more human-readable:

require 'minijava'

import "javax.swing.JFrame"
import "javax.swing.JButton"
import "java.awt.event.ActionListener"
import "java.lang.Integer"

class JFrame
  alias_method :add, :"add(java.awt.Component)java.awt.Component"
  alias_method :setSize, :"setSize(int,int)void"

  def size=(size)
    x, y = *size
    setSize(x.as(Integer), y.as(Integer))
  end

  class << self
alias_method :new_with_title, :"new(java.lang.String)javax.swing.JFrame"

    def new(title)
      new_with_title(title.to_java)
    end
  end
end

class JButton
  class << self
alias_method :new_with_text, :"new(java.lang.String)javax.swing.JButton;"

    def new(text)
      new_with_text(text.to_java)
    end
  end
end

frame = JFrame.new "This is my frame"
frame.show
frame.size = [300, 300]

button = JButton.new "Press me"
button.addActionListener(proc { button.setText("Pressed!".to_java) }.as(ActionListener))

frame.add button
frame.show


Charles Oliver Nutter wrote:
I got fed up with just thinking about a new Java layer and start to mock up a working prototype for a "minijava" library that skips all our Java integration code and does it all directly. A patch is attached for it.

The general idea behind this is to solve a few key problems with current Java scripting and provide a "raw" enough interface to build everything else upon.

1. No automatic coercions

No object types automatically coerce. Key types will gain "to_java" methods for a simple coercion and "as" methods for coercion to an explicit type. For example:

"foo".to_java # produces a wrapped java.lang.String

import "java.lang.Integer", "JInt"
123.as(JInt) # produces a wrapped java.lang.Integer

The problem solved here is manifold:

- performance cost of coercing types all the time

Because this model expect explicit coercion, you would typically coerce e.g. strings ahead of time and use them for a series of calls. This isn't a requirement, since some libraries/situations may call for implicit coercion, but at least with this you have an option to *not* do it, which did not exist before.

- inability to coerce to a specific Java type easily

There's no way to call a less-precise version of a primitive method if there's a more-precise version. For example, calling with Fixnum will always try long first, never getting to int versions. This has caused many subtle bugs.

- un-rubylike automatic coercion with no way to override (i.e. you can't define your own to_java easily and expect it to be called)

It should be possible to define a coercion for custom Ruby types entirely in Ruby. Currently, it can't be done easily.

2. All method signatures are bound separately

Check this out:

$ jruby -rminijava -e "t = Time.now; import 'java.lang.System'; p System.methods.grep /getProperty/" ["getProperty", "getProperty(java.lang.String,java.lang.String)java.lang.String", "getProperty(java.lang.String)java.lang.String"]

The no-signature method would eventually be the same "guessing" version we have now, but all signatures would be bound with the long names. These names are not directly invokable, of course, but can be "send" invoked or aliased to usable names.

Multiple problems are also solved here:

- Inability to call a specific signature

It requires aliasing or "send", but all signatures are always present and accessible. This is necessary when the guessing heuristic breaks down.

- Poor performance of overloaded method selection

In cases like StringBuffer, where there's dozens of overloads, method selection can be a slow process. We have improved it by building in caches mapping all the incoming parameter type combinations to the first selected method signature, but that's a crapload of caching if you're calling with many types...potentially even a cache leak right now. With the ability to skip the heuristic entirely, there's no caching necessary and far less logic required to do a call.

3. Class = Class, Module = Interface

This is mostly the same as what we have now, but it's all done actively rather than passively. In other words, when you import 'javax.swing.JFrame', it walks JFrame's superclasses and interfaces and all those types' methods and all those methods' return and parameter types, and so on. And it's still faster to import with minijava than with existing JI code.

~/NetBeansProjects/jruby ➔ jruby -rminijava -e "t = Time.now; import 'javax.swing.JTable'; puts Time.now - t; cls = JTable; until cls == Object; puts cls; cls = cls.superclass; end"
0.418
javax.swing.JTable
javax.swing.JComponent
java.awt.Container
java.awt.Component
java.lang.Object

~/NetBeansProjects/jruby ➔ jruby -rjava -e "t = Time.now; import 'javax.swing.JTable'; puts Time.now - t; cls = JTable; until cls == Object; puts cls; cls = cls.superclass; end"
0.511
Java::JavaxSwing::JTable
Java::JavaxSwing::JComponent
Java::JavaAwt::Container
Java::JavaAwt::Component
Java::JavaLang::Object
ConcreteJavaProxy
JavaProxy

Notice also the simpler hierarchy. When in Ruby land, Java types are rooted at Ruby Object, which is the immediate superclass of Java Object. Just like in Java land, Java Object is the immediate superclass of Ruby Object.

4. A few basic coercions so far...

String and Fixnum can simply coerce to Java String and Java Long, respectively. Fixnum also has an "as" method for other numeric Java types, and Proc has an "as" method that wraps it in a java.lang.reflect.Proxy for use as an interface implementation. These are very easy to add.

*****

So there's obviously more to do with this, but in just about 500 lines of code, it's already very functional. Here's a Swing example that takes advantage of most of the above features (note that most of the method rewiring would probably be handled by either third-party "nicifying" libraries or by standard libs we would ship to match current JI features).

And don't forget...this isn't using Java integration *at all*.

<CODE>
require 'minijava'

import "javax.swing.JFrame"
import "javax.swing.JButton"
import "java.awt.event.ActionListener"
import "java.lang.Integer"

class JFrame
  alias_method :add, :"add(Ljava/awt/Component;)Ljava/awt/Component;"
  alias_method :setSize, :"setSize(II)V"

  def size=(size)
    x, y = *size
    setSize(x.as(Integer), y.as(Integer))
  end

  class << self
alias_method :new_with_title, :"new(Ljava/lang/String;)Ljavax/swing/JFrame;"

    def new(title)
      new_with_title(title.to_java)
    end
  end
end

class JButton
  class << self
alias_method :new_with_text, :"new(Ljava/lang/String;)Ljavax/swing/JButton;"

    def new(text)
      new_with_text(text.to_java)
    end
  end
end

frame = JFrame.new "This is my frame"
frame.show
frame.size = [300, 300]

button = JButton.new "Press me"
button.addActionListener(proc { button.setText("Pressed!".to_java) }.as(ActionListener))

frame.add button
frame.show
</CODE>

Comments are requested.

- Charlie


------------------------------------------------------------------------

---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

   http://xircles.codehaus.org/manage_email


Reply via email to