S_a_k_Uの日記みたいなDB

~サクゥーと呼ばないで~

クラスパス上のクラスとパッケージを取得する

パッケージは、java.lang.Package#getPackages()メソッドで取得できそうだけど、クラスはできないっぽい?
Oracle Technology Network (OTN) Japan - 掲示板 » テクノロジー » Javaの部屋 > スレッド: パッケージ内のクラスの取得方法
にもそんなこと書いてる。
で、スレッド中にJavaのブートストラップクラスパス(System.getPropertie("sun.boot.class.path"))にあるライブラリのクラスを取得するコードがあったので、+ユーザクラスパス(System.getPropertie("java.class.path"))も取得するようにしてみた。
んで、Jarファイルしか相手にしてないみたいなので、クラスファイルのディレクトリからも取得できるようにしてみた。
パッケージもクラスも、複数のライブラリに存在する場合もあるので、マップの値はリストにしてます。
パッケージはともかく、クラスが複数存在する場合、ユーザクラスパス→ブートストラップクラスパスと処理しているので、その順番にlibClassListMapのリストにライブラリ名が格納されてると思います。多分。
クラスローダも同じ順番でクラスを探しに行ってると思うので、該当するクラスのライブラリは、libClassListMapのリストの最初のライブラリになると思います。多分。

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassViewer {

    
    // ライブラリ名(ディレクトリパス or Jarファイルパス)のリスト
    private List<String> libList = new ArrayList<String>();

    
    // クラス名とクラスが存在するライブラリ名のリストのマップ
    //   キー:クラス名
    //   値:ライブラリ名(ディレクトリパス or Jarファイルパス)のリスト
    private Map<String, List<String>> classLibListMap = new HashMap<String, List<String>>();


    // ライブラリ名とライブラリに包含されるクラス名のリストのマップ
    //   キー:ライブラリ名(ディレクトリパス or Jarファイルパス)
    //   値:クラス名のリスト
    private Map<String, List<String>> libClassListMap = new HashMap<String, List<String>>();


    // パッケージ名とパッケージが存在するライブラリ名のリストのマップ
    //   キー:パッケージ名
    //   値:ライブラリ名(ディレクトリパス or Jarファイルパス)のリスト
    private Map<String, List<String>> packageLibListMap = new HashMap<String, List<String>>();


    // ライブラリ名とライブラリが包含するパッケージ名のリストのマップ
    //   キー:ライブラリ名
    //   値:パッケージ名のリスト
    private Map<String, List<String>> libPackageListMap = new HashMap<String, List<String>>();
    
    
    // デフォルトコンストラクタ
    public ClassViewer() {
        super();
    }
    
    
    public static void main(String [] args) {
        ClassViewer viewer = new ClassViewer();
        viewer.setup();
        List<String> libList = viewer.getLibList();
        for ( int i = 0 ; i < libList.size() ; i++ ) {
            String libname = libList.get(i);
            System.out.println("【クラス】ライブラリ:" + libname);
            List<String> classList = viewer.getLibClassList(libname);
            for ( int j = 0 ; j < classList.size() ; j++ ) {
                System.out.println(classList.get(j));                
            }
            System.out.println("【パッケージ】ライブラリ:" + libname);
            List<String> packageList = viewer.getLibPackageList(libname);
            for ( int j = 0 ; j < packageList.size() ; j++ ) {
                System.out.println(packageList.get(j));                
            }
        }
        System.out.println("【全てのクラス】");
        List<String> classList = viewer.getAllClassList();
        for ( int j = 0 ; j < classList.size() ; j++ ) {
            System.out.println(classList.get(j));                
        }
        System.out.println("【全てのパッケージ】");
        List<String> packageList = viewer.getAllPackageList();
        for ( int j = 0 ; j < packageList.size() ; j++ ) {
            System.out.println(packageList.get(j));                
        }
    }
    

    // ライブラリ名のリストを取得する
    public List<String> getLibList() {
        List<String> ret = new ArrayList<String>();
        if ((this.libList != null) && (this.libList.size() > 0)) {
            ret.addAll(this.libList);
        }
        return ret;
    }

    
    // クラスが存在するライブラリ名のリストを取得する
    public List<String> getClassLibList(String classname) {
        List<String> ret = new ArrayList<String>();
        if ((this.classLibListMap.get(classname) != null) && (this.classLibListMap.get(classname).size() > 0)) {
            ret.addAll(this.classLibListMap.get(classname));
        }
        return ret;
    }

    
    // ライブラリに包含されるクラス名のリストを取得する
    public List<String> getLibClassList(String libname) {
        List<String> ret = new ArrayList<String>();
        if ((this.libClassListMap.get(libname) != null) && (this.libClassListMap.get(libname).size() > 0)) {
            ret.addAll(this.libClassListMap.get(libname));
        }
        return ret;
    }

    
    // パッケージが存在するライブラリ名のリストを取得する
    public List<String> getPackageLibListMap(String packagename) {
        List<String> ret = new ArrayList<String>();
        if ((this.packageLibListMap.get(packagename) != null) && (this.packageLibListMap.get(packagename).size() > 0)) {
            ret.addAll(this.packageLibListMap.get(packagename));
        }
        return ret;
    }

    
    // ライブラリが包含するパッケージ名のリストを取得する
    public List<String> getLibPackageList(String libname) {
        List<String> ret = new ArrayList<String>();
        if ((this.libPackageListMap.get(libname) != null) && (this.libPackageListMap.get(libname).size() > 0)) {
            ret.addAll(this.libPackageListMap.get(libname));
        }
        return ret;
    }
    

    // 全てのクラス名のリストを取得する
    public List<String> getAllClassList() {
        List<String> ret = new ArrayList<String>();
        Set<String> set = this.classLibListMap.keySet();
        Object [] clazzs = set.toArray();
        Arrays.sort(clazzs);
        for ( int i = 0 ; i < clazzs.length ; i++ ) {
            ret.add((String) clazzs[i]);
        }
        return ret;
    }
    

    // 全てのクラス名のリストを取得する
    public List<String> getAllPackageList() {
        List<String> ret = new ArrayList<String>();
        Set<String> set = this.packageLibListMap.keySet();
        Object [] packages = set.toArray();
        Arrays.sort(packages);
        for ( int i = 0 ; i < packages.length ; i++ ) {
            ret.add((String) packages[i]);
        }
        return ret;
    }

    
    // 各リスト、マップをクリアする
    protected void clear() {
        this.libList.clear();
        this.classLibListMap.clear();
        this.libClassListMap.clear();
        this.packageLibListMap.clear();
        this.libPackageListMap.clear();
    }

    
    // ライブラリからクラスとパッケージを検索する
    public void setup() {
        clear();
        parseLib(System.getProperties().getProperty("java.class.path"));
        parseLib(System.getProperties().getProperty("sun.boot.class.path"));
        for ( int i = 0 ; i < this.libList.size() ; i++ ) {
            parseClass(this.libList.get(i));
        }
    }

    
    // ライブラリを取得する
    protected void parseLib(String libname) {
        if ((libname != null) && (libname.length() > 0)) {
            StringTokenizer st = new StringTokenizer(libname, ";");
            while(st.hasMoreTokens()) {
                this.libList.add(st.nextToken());
            }
        }
    }

    
    // クラスを取得する
    protected void parseClass(String libname) {
        if (isJarFile(libname) == true) {
            parseClassJarFile(libname);
        } else if (isClassDir(libname) == true) {
            parseClassClassDir(libname, libname);
        }
    }

    
    // jarファイルかどうか?
    public boolean isJarFile(String libname) {
        File file = new File(libname);
        if ((file.isFile() == true) && (file.getName().toLowerCase().endsWith(".jar"))) {
            return true;
        }
        return false;
    }

    
    // ディレクトリパスかどうか?
    public boolean isClassDir(String libname) {
        File file = new File(libname);
        if (file.isDirectory() == true) {
            return true;
        }
        return false;
    }

    
    // jarファイルからクラスを取得する
    protected void parseClassJarFile(String libname) {
        try {
            JarFile jar = new JarFile(libname);
            Enumeration<JarEntry> e = jar.entries();
            while (e.hasMoreElements()) {
                JarEntry j = (JarEntry) e.nextElement();
                putClassName(j.getName(), libname);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    
    // ディレクトリパスからクラスを取得する
    protected void parseClassClassDir(String dir, String libname) {
        File [] files = new File(dir).listFiles();
        for ( int i = 0 ; i < files.length ; i++ ) {
            if (files[i].isDirectory() == true) {
                // ディレクトリを再帰的に処理する
                parseClassClassDir(files[i].getAbsolutePath(), libname);
            } else {
                String filepath = files[i].getAbsolutePath();
                // ライブラリディレクトリ以下からファイル名を切り出す
                String classfile = filepath.substring(libname.length() + 1, filepath.length());
                putClassName(classfile, libname);
            }
        }
    }

    
    // クラス名から各リスト、マップに値を追加する
    protected void putClassName(String classname, String libname) {
        String name = cnvClassName(classname);
        if (name == null) {
            return;
        }
        // ライブラリに存在するクラス名のリストを取得する
        List<String> classList = this.libClassListMap.get(libname);
        if (classList == null) {
            classList = new ArrayList<String>();
            this.libClassListMap.put(libname, classList);
        }
        // 既にクラス名を追加済みかどうか
        boolean exist = false;
        for ( int i = 0 ; i < classList.size() ; i++ ) {
            if (name.equals(classList.get(i)) == true) {
                exist = true;
                break;
            }
        }
        if (exist == false) {
            // ライブラリに存在するクラス名のリストにクラス名を追加する
            classList.add(name);
            // クラスを包含するライブラリ名のリストを取得する
            List<String> libList = this.classLibListMap.get(name);
            if (libList == null) {
                libList = new ArrayList<String>();
                this.classLibListMap.put(name, libList);
            }
            // パッケージを包含するライブラリ名のリストにライブラリ名を追加する
            libList.add(libname);
        }
        // パッケージに関するマップに値を追加する
        putPackageName(name, libname);
    }

    
    // クラス名からパッケージに関する各リスト、マップに値を追加する
    protected void putPackageName(String classname, String libname) {
        String name = cnvPackageName(classname);
        // ライブラリに存在するパッケージ名のリストを取得する
        List<String> packageList = this.libPackageListMap.get(libname);
        if (packageList == null) {
            packageList = new ArrayList<String>();
            this.libPackageListMap.put(libname, packageList);
        }
        // 既にパッケージ名を追加済みかどうか
        boolean exist = false;
        for ( int i = 0 ; i < packageList.size() ; i++ ) {
            if (name.equals(packageList.get(i)) == true) {
                exist = true;
                break;
            }
        }
        if (exist == false) {
            // ライブラリに存在するパッケージ名のリストにパッケージ名を追加する
            packageList.add(name);
            // パッケージを包含するライブラリ名のリストを取得する
            List<String> libList = this.packageLibListMap.get(name);
            if (libList == null) {
                libList = new ArrayList<String>();
                this.packageLibListMap.put(name, libList);
            }
            // パッケージを包含するライブラリ名のリストにライブラリ名を追加する
            libList.add(libname);
        }
    }
    
    
    // クラス名(xxx.yyy.zzz.classname)に変換する
    protected String cnvClassName(String val) {
        if (val != null) {
            // ファイルセパレータとスラッシュをドットに変換
            String name = val.replace(System.getProperty("file.separator"), ".").replace("/", ".");
            // .classを除去
            if (name.toLowerCase().endsWith(".class") == true) {
                return name.substring(0, name.length() - 6);
            }
        }
        return null;
    }
    
    
    // パッケージ名(xxx.yyy.zzz)に変換する
    protected String cnvPackageName(String val) {
        if (val != null) {
            return val.substring(0, val.lastIndexOf("."));
        }
        return null;
    }
    

}

こんなことできるクラスがねぇんか?と探してる最中に、Javassistってなライブラリの存在があるのを知ってみたり。
これはこれで、なんかの時に使えるんかとか。
エスエムジー株式会社 SMG > WEBワークショップ > Javassistチュートリアル