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