Seasar先生

ということでSeasar先生にもお伺いを立ててみた。Seasar先生には昔JTAの実装でお世話になった記憶がある。
まずSeasar2のサイトからアーカイブをダウンロード。
おもむろにJar*で検索すると、いくつか引っかかるが、見たところorg.seasar.framework.util.ResourceTraversalと
org.seasar.framework.util.ResourceTraversalが正解っぽい。
いずれのクラスもforEachというstaticメソッドが定義されていて、引数にJarFileとハンドラーインターフェースを取る
ようになっている。

処理の内容は、JarFileからentryを取り出して、ディレクトリでなければハンドラーインターフェースにストリームや
クラス情報を渡している。ディレクトリじゃないかどうかは、Seasar2の場合JarEntryのisDirectory()メソッドを使用している。

というか、JarEntryにisDirectory()ってメソッドあるじゃんorz

まあいい、とりあえずディレクトリの一覧を取るメソッドはないのは確実だし。

結局Seasarの方もゴリゴリ回すのは同じだけど、eclipseとはやり方がちょっと違う。
XMLでいうとeclipseはDOM、Seasar2のほうはSAXのようなやり方になっている。

大きく違うのは、リソースの途中までのパスの扱いで、eclipseの方はリソースの途中までのパス、
例えば/nikoniko/douga/GJ.classというEntryがある場合、「/nikoniko」「/douga」「/GJ.class」を
パースして保持する(/nikonikoと/dougaはZipDirectory、/dougaはZipFileクラスとして保持)が、
Seasar2の場合はあくまで/nikoniko/douga/GJ.classを1つとして扱う。

今回ほしいのは、/nikoniko/douga/GJ.classに対して

 /nikoniko/
 /nikoniko/douga/
 /nikoniko/douga/GJ.class

のパスと、ディレクトリでないかどうかの情報なので、今回の目的を達成するには、
次のような実装がいいのではという結論に達した。

 ・forEach的なメソッドで、Jar内部のEntryを回す。
 ・Entryのパスを取り、区切りごとに細かくパースする。
 ・パース結果をハンドラーに通知。この際、パースしたパス、ディレクトリかどうかのフラグ、InputStreamを一緒に渡す。
 ・ハンドラー実装クラスでは引数を使ってJar内部の構成を作成。Jarの内部データをいつ読むかは実装次第。

とこんな感じだろうか。

とりあえず休憩終了。。。というかそろそろ帰らないと。
実装は後日。

以下テストソース(Seasar2ありがとう)

package jartest;

import java.io.File;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import org.seasar.framework.util.InputStreamUtil;
import org.seasar.framework.util.JarFileUtil;

public class JarSAX {

	public static void main(String[] args) {
		try{
			JarFile jarFile = new JarFile(new File("test.war"));
			
			forEach(jarFile,new ResourceHandler(){
				public void processResource(String path, InputStream is) {
					//System.out.println(path);
				}
			});
		}catch(Exception e){
			e.printStackTrace();
		}
	}
    /**
     * リソースを処理するインターフェースです。
     * 
     */
    public interface ResourceHandler {
        /**
         * リソースを処理します。
         * 
         * @param path
         * @param is
         */
        void processResource(String path, InputStream is);
    }
    /**
     * トラバースします。
     * 
     * @param jarFile
     * @param handler
     */
    public static void forEach(final JarFile jarFile,
            final ResourceHandler handler) {
        final Enumeration enumeration = jarFile.entries();
        while (enumeration.hasMoreElements()) {
            final JarEntry entry = (JarEntry) enumeration.nextElement();
            if (!entry.isDirectory()) {
                final String entryName = entry.getName().replace('\\', '/');
                final InputStream is = JarFileUtil.getInputStream(jarFile,
                        entry);
                try {
                    handler.processResource(entryName, is);
                } finally {
                    InputStreamUtil.close(is);
                }
            }
        }
    }
}