以前、libxml-rubyで [BUG] object allocation during garbage collection phase で、元々libxml-rubyを使って書いてたコードをjavaのDOM/XPath APIに置き換えるために、ラッパを書いた。
のだけど、
「JRuby徹底入門」という本を読んだら、javaのクラス(のproxy)にメソッドを後付けするにはJavaUtilities.extend_proxyを使う、という例が載ってた。
なるほど便利。
なんでも実行時に決定できるってすごいことだな。
jruby:1.1.3
Sun Java: 1.6.0_05
OS:CentOS 5.2
$KCODE='u'>> Home
require 'java'
# org.w3c.dom.Documentにlibxml-ruby風I/F追加
JavaUtilities.extend_proxy('org.w3c.dom.Document') do
def root
self.documentElement
end
end
# org.w3c.dom.Nodeにlibxml-ruby風I/F追加
JavaUtilities.extend_proxy('org.w3c.dom.Node') do
@@xpath = nil
def to_s
self.nodeValue.to_s
end
def next
self.nextSibling
end
def text?
self.nodeType == org.w3c.dom.Node::TEXT_NODE
end
def [](name)
self.getAttribute(name)
end
def parent
self.parentNode
end
def child
self.firstChild
end
# XPathオブジェクトを返す
#
# TODO: XPathオブジェクト1つで使いまわしてみたが・・・
# evaluateの都度生成よりはいいような気がするがどうだろう
def get_xpath
if !@@xpath
factory = javax.xml.xpath.XPathFactory.newInstance()
@@xpath = factory.newXPath()
end
@@xpath
end
# 指定されたノードに対するxpath式のクエリ結果を返す
#
# expr::
# XPath式
def find(expr)
nodelist = self.get_xpath.evaluate(expr, self, javax.xml.xpath.XPathConstants::NODESET)
ret = []
for i in 0..(nodelist.length - 1)
ret.push(nodelist.item(i))
end
ret
end
end
# 指定されたファイルをopenしてXML文書を返す
#
# fn_in::
# ファイル名。nilの場合、stdinを適用
def build_doc(fn_in)
st = java.lang.System.in
if fn_in
st = java.io.FileInputStream.new(fn_in)
end
factory = javax.xml.parsers.DocumentBuilderFactory.newInstance()
builder = factory.newDocumentBuilder()
builder.parse(st)
end
doc = build_doc("test.xml")
doc.find("//entity").each { |e|
puts e["name"]
}