reflectでクラスブラウザ

 java.lang.reflectを使って、クラスブラウザを書いてみた。
 ■ソースコード
 次のようなコードを書いてみた。
 クラスパスのJarファイルを検索し、クラスの木構造を構築する。
 文字列で順序付けした木構造を得るため、DefaultTreeModelのTreeNodeにはインナークラスSortedTreeNodeを用いている。

package test;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;
import java.util.zip.ZipFile;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTree;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.MutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

public class JClassBrowserPanel extends JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private Map<String,DefaultMutableTreeNode> map
		=new HashMap<String,DefaultMutableTreeNode>();
	private String classpath = System.getProperty("java.class.path");
	private String homepath = System.getProperty("java.home");
	private String pathsep = File.pathSeparator;
	private JTree tree;
	private JTable cons,field,method;
	private JPopupMenu pop;
	private DefaultTreeModel model;
	private SortedTreeNode root;

	public JClassBrowserPanel(){
		super(new BorderLayout());
		root=new SortedTreeNode("root");
		model=new DefaultTreeModel(root);
		initPanel();
		Runnable r=new Runnable(){
			public void run(){
				parse();
				Runnable rr=new Runnable(){
					public void run(){
						TreeNode last=root.getLastChild();
						tree.expandPath(new TreePath(new TreeNode[]{root,last}));
					}
				};
				javax.swing.SwingUtilities.invokeLater(rr);
			}
		};
		new Thread(r).start();
	}
	
	private void initPanel(){
		JSplitPane sp=new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		tree=new JTree(root);
		tree.addMouseListener(new TreeMouse());
		tree.setModel(model);
		JScrollPane sc=new JScrollPane(tree);
		sp.setLeftComponent(sc);
		JPanel panel=new JPanel(new BorderLayout());
		JSplitPane spl0=new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		JSplitPane spl1=new JSplitPane(JSplitPane.VERTICAL_SPLIT);
		panel.add(spl0,BorderLayout.CENTER);
		spl0.setRightComponent(spl1);
		JPanel u=new JPanel(new BorderLayout());
		cons=new JTable(new TModel());
		((DefaultTableModel)cons.getModel()).setColumnCount(1);
		((DefaultTableModel)cons.getModel()).setRowCount(0);
		((DefaultTableModel)cons.getModel()).setColumnIdentifiers(new String[]{"Constractor"});
		JScrollPane sc0=new JScrollPane(cons);
		u.add(sc0,BorderLayout.CENTER);
		spl0.setLeftComponent(u);
		JPanel m=new JPanel(new BorderLayout());
		field=new JTable(new TModel());
		((DefaultTableModel)field.getModel()).setColumnCount(1);
		((DefaultTableModel)field.getModel()).setRowCount(0);
		((DefaultTableModel)field.getModel()).setColumnIdentifiers(new String[]{"Field"});
		JScrollPane sc1=new JScrollPane(field);
		m.add(sc1,BorderLayout.CENTER);
		spl1.setLeftComponent(m);
		JPanel d=new JPanel(new BorderLayout());
		method=new JTable(new TModel());
		((DefaultTableModel)method.getModel()).setColumnCount(1);
		((DefaultTableModel)method.getModel()).setRowCount(0);
		((DefaultTableModel)method.getModel()).setColumnIdentifiers(new String[]{"Method"});
		JScrollPane sc2=new JScrollPane(method);
		d.add(sc2,BorderLayout.CENTER);
		spl1.setRightComponent(d);	
		sp.setRightComponent(panel);
		add(sp,BorderLayout.CENTER);
		sp.setDividerSize(3);
		sp.setDividerLocation(200);
		spl0.setDividerSize(3);
		spl0.setDividerLocation(100);
		spl1.setDividerSize(3);
		spl1.setDividerLocation(100);
		pop=new JPopupMenu();
		JMenuItem it=new JMenuItem("send");
		it.addActionListener(new ActionListener(){
			public void actionPerformed(ActionEvent arg0) {
				TreePath p=tree.getSelectionPath();
				if(p==null)return;
			}
		});
		pop.add(it);
	}
	
	private void parse(){
		StringTokenizer tk = new StringTokenizer(classpath, pathsep);
		while (tk.hasMoreTokens()) {
			File file = new File(tk.nextToken());
			if (file.exists()) {
				if(file.isDirectory()){
					searchDir(file);
				}else{
					try{
						searchZip(new ZipFile(file));
					}catch(IOException ie){}
				}
			} 
		}
		
		tk = new StringTokenizer(homepath, pathsep);
		while (tk.hasMoreTokens()) {
			File file = new File(tk.nextToken());
			if (file.exists()) {
				if(file.isDirectory()){
					searchDir(file);
				}else{
					try{
						searchZip(new ZipFile(file));
					}catch(IOException ie){}
				}
			} 
		}
	}
	
	private void searchDir(File dir) {
		File[] f=dir.listFiles();
		for(int i=0;i<f.length;i++){
			if(f[i].isDirectory()){
				searchDir(f[i]);
			}else{
				try{
					searchZip(new ZipFile(f[i]));
				}catch(IOException ie){}
			}
		}
	}
	
	private void searchZip(ZipFile zip) {
		Enumeration<?> e=zip.entries();
		while(e.hasMoreElements()){
			String name=e.nextElement().toString();
			if(name.contains(".class")){
				String[] str=name.split("/");
				if(str[str.length-1].indexOf("$")!=-1)continue;
				regedit(str);
			}
		}
	}
	
	private void regedit(String[] path){
		DefaultMutableTreeNode parent=root;
		String tmp=null;
		for(int i=0;i<path.length;i++){
			if(tmp==null){
				tmp=path[i];
			}else{
				tmp=tmp+"."+path[i];
			}
			DefaultMutableTreeNode m=map.get(tmp);
			if(m==null){
				DefaultMutableTreeNode x=new SortedTreeNode(path[i]);
				map.put(tmp,x);
				model.insertNodeInto(x, parent, 0);
				parent=x;
			}else{
				parent=m;
			}
		}
	}
	
	private String getClassName(TreePath p){
		int n=p.getPathCount();
		String str="";
		for(int i=1;i<n;i++){
			String ss=((DefaultMutableTreeNode)p.getPathComponent(i)).getUserObject().toString();
			if(i==1){
				str=ss;
			}else{
				str=str+"."+ss;
			}
		}
		str=str.replace(".class","");
		return str;
	}
	
	private class SortedTreeNode extends DefaultMutableTreeNode{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;
		private Set<String> set;
		
		public SortedTreeNode(Object obj){
			super(obj);
			set=new TreeSet<String>();
		}

		@Override
		public void add(MutableTreeNode arg0) {
			set.add(arg0.toString());
			int id=index(arg0.toString());
			super.insert(arg0, id);
		}
		
		@Override
		public void insert(MutableTreeNode arg0, int arg1) {
			this.add(arg0);
		}

		@Override
		public void remove(int arg0) {
			super.remove(arg0);
		}

		@Override
		public void remove(MutableTreeNode arg0) {
			super.remove(arg0);
			set.remove(arg0.toString());
		}

		@Override
		public void removeAllChildren() {
			super.removeAllChildren();
			set.clear();
		}

		private int index(String arg){
			String[] st=set.toArray(new String[set.size()]);
			for(int i=0;i<st.length;i++){
				if(st[i].equals(arg))return i;
			}
			return -1;
		}
		
	}
	
	private class TreeMouse implements MouseListener{
		
		public void mouseClicked(MouseEvent arg0) {
			switch(arg0.getButton()){
			case MouseEvent.BUTTON1:
				TreePath p=tree.getSelectionPath();
				if(p==null)return;
				DefaultMutableTreeNode tn=(DefaultMutableTreeNode)p.getLastPathComponent();
				if(tn.isLeaf()){
					String name=getClassName(p);
					try{
						Class<?> c=Class.forName(name);
						Constructor<?>[] cn=c.getConstructors();
						String[] names=new String[cn.length];
						for(int i=0;i<cn.length;i++){
							names[i]=cn[i].getName()+"(";
							Class<?>[] cc=cn[i].getParameterTypes();
							for(int j=0;j<cc.length;j++){
								if(j==0){
									names[i]=names[i]+cc[j].getName();
								}else{
									names[i]=names[i]+","+cc[j].getName();
								}
							}
							names[i]=names[i]+")";
						}
						Arrays.sort(names);
						DefaultTableModel tm=(DefaultTableModel)cons.getModel();
						tm.setRowCount(cn.length);
						for(int i=0;i<cn.length;i++){
							tm.setValueAt(names[i], i, 0);
						}
						Field[] fn=c.getFields();
						names=new String[fn.length];
						for(int i=0;i<fn.length;i++)names[i]=fn[i].getName()+" as "+fn[i].getType();
						Arrays.sort(names);
						tm=(DefaultTableModel)field.getModel();
						tm.setRowCount(fn.length);
						for(int i=0;i<fn.length;i++){
							tm.setValueAt(names[i], i, 0);
						}
						Method[] mn=c.getMethods();
						names=new String[mn.length];
						for(int i=0;i<mn.length;i++){
							names[i]=mn[i].getName()+"(";
							Class<?>[] cc=mn[i].getParameterTypes();
							for(int j=0;j<cc.length;j++){
								if(j==0){
									names[i]=names[i]+cc[j].getName();
								}else{
									names[i]=names[i]+","+cc[j].getName();
								}
							}
							names[i]=names[i]+")";
						}
						Arrays.sort(names);
						tm=(DefaultTableModel)method.getModel();
						tm.setRowCount(mn.length);
						for(int i=0;i<mn.length;i++){
							tm.setValueAt(names[i], i, 0);
						}
					}catch(ClassNotFoundException ce){}
				}
				break;
			}
		}

		public void mouseEntered(MouseEvent arg0) {}
		public void mouseExited(MouseEvent arg0) {}
		public void mousePressed(MouseEvent arg0) {}
		public void mouseReleased(MouseEvent arg0) {}
	}
	
	private class TModel extends DefaultTableModel{
		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		@Override
		public boolean isCellEditable(int arg0, int arg1) {
			return false;
		}
	}
	
	public static void main(String[] args){
		JFrame f=new JFrame();
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.getContentPane().setLayout(new BorderLayout());
		JClassBrowserPanel cb=new JClassBrowserPanel();
		f.getContentPane().add(cb,BorderLayout.CENTER);
		f.setSize(640, 400);
		f.setTitle("Test");
		f.setVisible(true);
	}
}

 ■結果
 実行結果は、以下のとおり。