読者です 読者をやめる 読者になる 読者になる
無料で使えるシステムトレードフレームワーク「Jiji」 をリリースしました!

・OANDA Trade APIを利用した、オープンソースのシステムトレードフレームワークです。
・自分だけの取引アルゴリズムで、誰でも、いますぐ、かんたんに、自動取引を開始できます。

jar内のクラスの型階層を表示するRubyスクリプト

Ruby Java クラス解析機

jar内のクラスの型階層を表示するスクリプトです。たとえばaopalliance.jarを対象に実行すると、↓のようなツリーを表示します。

---
interface org.aopalliance.aop.Advice
└interface org.aopalliance.intercept.Interceptor
 ├interface org.aopalliance.intercept.MethodInterceptor
 └interface org.aopalliance.intercept.ConstructorInterceptor

---
(unknown) java.lang.RuntimeException
└class org.aopalliance.aop.AspectException

---
interface org.aopalliance.intercept.Joinpoint
└interface org.aopalliance.intercept.Invocation
 ├interface org.aopalliance.intercept.ConstructorInvocation
 └interface org.aopalliance.intercept.MethodInvocation

内部的には、javaclassrubyzipを使ってjarを解析し、階層情報を収集・表示しています。

実装

スクリプトのソースは以下。javaclassrubyzipに依存しているので、インストールしておく必要があります。どちらもgem経由でインストールできます。

javaclassとrubyzipのインストール手順

rubygemのバージョンが1.1.1でない場合、バージョンアップする必要があります。

$ gem install rubygems-update
$ gem update --system

gemの取得先に「gems.github.com」を追加。

$ gem sources -a http://gems.github.com

javaclassとrubyzipをインストール。(githubのgemにはユーザー名が先頭につくらしい。むー)

$ gem install unageanu-javaclass
$ gem install rubyzip
ソース

type_hierarchy.rb :

#!/usr/bin/ruby

# jar内のクラスファイルの型階層を表示する。
#
# ./type_hierarchy.rb <jarファイル>
#

require "rubygems"
require "javaclass"
require "zip/zip"
require "kconv"

# ZipInputStreamにはgetcが実装されていないので、追加する。
module Zip
  class ZipInputStream
    def getc
      read(1)[0]
    end
  end
end

# Zipエントリ内のクラス一覧を列挙して解析する。
def each_class ( zip_file, &block ) 
  Zip::ZipFile.foreach(zip_file) {|entry|
    next unless entry.file?
    next unless entry.name =~ /.*\.class$/
    entry.get_input_stream {|io|
      jc = JavaClass.from io
      block.call( jc ) if block_given?
    }
  }
end

# 型階層
class TypeHierarchy
  def initialize
    @classes = {}
  end
  def <<( jc )
    (jc.interfaces << jc.super_class).each {|parent|
      next if parent == nil || parent == "java.lang.Object"
      @classes[parent] = Type.new(parent) unless @classes.key? parent
      @classes[parent] << jc.name
    }
    name = jc.name
    @classes[name] = Type.new(name) unless @classes.key? name
    @classes[name].java_class = jc
  end
  def to_s
    strs = ""
    @classes.each{|k,v|
      jc = v.java_class
      next if jc != nil && jc.super_class != nil && jc.super_class != "java.lang.Object"
      next if jc != nil && jc.interfaces.length > 0
      strs << "---\n" <<  v.node_to_string( "", @classes ) << "\n"
    }
    return strs
  end
end

# 型
class Type
  def initialize( name, java_class=nil  )
    @name = name
    @implementors = []
    @java_class = java_class
  end
  def <<(implementor)
    @implementors << implementor
  end
  def node_to_string( indent, pool )
    str = indent.dup
    str << ( java_class == nil ? "(unknown) " : java_class.access_flag.type + " " ) 
    str << name
    str << "\n"
    child_indent = indent.gsub(//, "").gsub(//, " ")
    @implementors.each_index {|i|
       next if implementors[i] == nil
       tmp = child_indent + ( i >= implementors.length-1 ? "" : "" )
       str <<  pool[implementors[i]].node_to_string( tmp, pool )
    }
    return str
  end
  attr :name, true
  attr :java_class, true
  attr :implementors, true
end

th = TypeHierarchy.new
each_class( ARGV[0] ){|jc|
  th << jc
}
puts th.to_s.tosjis
実行

引数でjarを指定すればOK

$ ./type_hierarchy.rb <jarファイル>