/*****************************************************************************
* *
* This file is part of the BeanShell Java Scripting distribution. *
* Documentation and updates may be found at http://www.beanshell.org/ *
* *
* Sun Public License Notice: *
* *
* The contents of this file are subject to the Sun Public License Version *
* 1.0 (the "License"); you may not use this file except in compliance with *
* the License. A copy of the License is available at http://www.sun.com *
* *
* The Original Code is BeanShell. The Initial Developer of the Original *
* Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright *
* (C) 2000. All Rights Reserved. *
* *
* GNU Public License Notice: *
* *
* Alternatively, the contents of this file may be used under the terms of *
* the GNU Lesser General Public License (the "LGPL"), in which case the *
* provisions of LGPL are applicable instead of those above. If you wish to *
* allow use of your version of this file only under the terms of the LGPL *
* and not to allow others to use your version of this file under the SPL, *
* indicate your decision by deleting the provisions above and replace *
* them with the notice and other provisions required by the LGPL. If you *
* do not delete the provisions above, a recipient may use your version of *
* this file under either the SPL or the LGPL. *
* *
* Patrick Niemeyer ([email protected]) *
* Author of Learning Java, O'Reilly & Associates *
* http://www.pat.net/~pat/ *
* *
*****************************************************************************/
package bsh.util;
import java.util.*;
import java.util.zip.*;
import javax.swing.*;
import javax.swing.tree.*;
import javax.swing.event.*;
import javax.swing.border.*;
import java.io.*;
import java.awt.*;
import java.lang.reflect.*;
import java.util.List;
// For string related utils
import bsh.BshClassManager;
import bsh.classpath.BshClassPath;
import bsh.classpath.ClassPathListener;
import bsh.ClassPathException;
import bsh.StringUtil;
import bsh.ConsoleInterface;
import bsh.classpath.ClassManagerImpl;
/**
A simple class browser for the BeanShell desktop.
*/
public class ClassBrowser extends JSplitPane
implements ListSelectionListener, ClassPathListener
{
BshClassPath classPath;
BshClassManager classManager;
// GUI
JFrame frame;
JInternalFrame iframe;
JList classlist, conslist, mlist, fieldlist;
PackageTree ptree;
JTextArea methodLine;
JTree tree;
// For JList models
String [] packagesList;
String [] classesList;
Constructor [] consList;
Method [] methodList;
Field [] fieldList;
String selectedPackage;
Class selectedClass;
private static final Color LIGHT_BLUE = new Color(245,245,255);
public ClassBrowser() {
this( BshClassManager.createClassManager( null/*interpreter*/ ) );
}
public ClassBrowser( BshClassManager classManager ) {
super( VERTICAL_SPLIT, true );
this.classManager = classManager;
setBorder(null);
javax.swing.plaf.SplitPaneUI ui = getUI();
if(ui instanceof javax.swing.plaf.basic.BasicSplitPaneUI) {
((javax.swing.plaf.basic.BasicSplitPaneUI)ui).getDivider()
.setBorder(null);
}
}
String [] toSortedStrings ( Collection c ) {
List l = new ArrayList( c );
String [] sa = (String[])(l.toArray( new String[0] ));
return StringUtil.bubbleSort(sa);
}
void setClist( String packagename ) {
this.selectedPackage = packagename;
Set set = classPath.getClassesForPackage( packagename );
if ( set == null )
set = new HashSet();
// remove inner classes and shorten class names
List list = new ArrayList();
Iterator it = set.iterator();
while (it.hasNext()) {
String cname = (String)it.next();
if ( cname.indexOf("$") == -1 )
list.add( BshClassPath.splitClassname( cname )[1] );
}
classesList = toSortedStrings(list);
classlist.setListData( classesList );
//setMlist( (String)classlist.getModel().getElementAt(0) );
}
String [] parseConstructors( Constructor [] constructors ) {
String [] sa = new String [ constructors.length ] ;
for(int i=0; i< sa.length; i++) {
Constructor con = constructors[i];
sa[i] = StringUtil.methodString(
con.getName(), con.getParameterTypes() );
}
//return bubbleSort(sa);
return sa;
}
String [] parseMethods( Method [] methods ) {
String [] sa = new String [ methods.length ] ;
for(int i=0; i< sa.length; i++)
sa[i] = StringUtil.methodString(
methods[i].getName(), methods[i].getParameterTypes() );
//return bubbleSort(sa);
return sa;
}
String [] parseFields( Field[] fields ) {
String [] sa = new String [ fields.length ] ;
for(int i=0; i< sa.length; i++) {
Field f = fields[i];
sa[i] = f.getName();
}
return sa;
}
Constructor [] getPublicConstructors( Constructor [] constructors ) {
Vector v = new Vector();
for(int i=0; i< constructors.length; i++)
if ( Modifier.isPublic(constructors[i].getModifiers()) )
v.addElement( constructors[i] );
Constructor [] ca = new Constructor [ v.size() ];
v.copyInto( ca );
return ca;
}
Method [] getPublicMethods( Method [] methods ) {
Vector v = new Vector();
for(int i=0; i< methods.length; i++)
if ( Modifier.isPublic(methods[i].getModifiers()) )
v.addElement( methods[i] );
Method [] ma = new Method [ v.size() ];
v.copyInto( ma );
return ma;
}
Field[] getPublicFields( Field [] fields ) {
Vector v = new Vector();
for(int i=0; i< fields.length; i++)
if ( Modifier.isPublic(fields[i].getModifiers()) )
v.addElement( fields[i] );
Field [] fa = new Field [ v.size() ];
v.copyInto( fa );
return fa;
}
void setConslist( Class clas ) {
if ( clas == null ) {
conslist.setListData( new Object [] { } );
return;
}
consList = getPublicConstructors( clas.getDeclaredConstructors() );
conslist.setListData( parseConstructors(consList) );
}
void setMlist( String classname )
{
if ( classname == null )
{
mlist.setListData( new Object [] { } );
setConslist( null );
setClassTree( null );
return;
}
Class clas;
try {
if ( selectedPackage.equals("<unpackaged>") )
selectedClass = classManager.classForName( classname );
else
selectedClass = classManager.classForName(
selectedPackage + "." + classname );
} catch ( Exception e ) {
System.err.println(e);
return;
}
if ( selectedClass == null ) {
// not found?
System.err.println("class not found: "+classname);
return;
}
methodList = getPublicMethods( selectedClass.getDeclaredMethods() );
mlist.setListData( parseMethods(methodList) );
setClassTree( selectedClass );
setConslist( selectedClass );
setFieldList( selectedClass );
}
void setFieldList( Class clas ) {
if ( clas == null ) {
fieldlist.setListData( new Object [] { } );
return;
}
fieldList = getPublicFields(clas.getDeclaredFields());
fieldlist.setListData( parseFields(fieldList) );
}
void setMethodLine( Object method ) {
methodLine.setText( method==null ? "" : method.toString() );
}
void setClassTree( Class clas ) {
if ( clas == null ) {
tree.setModel( null );
return;
}
MutableTreeNode bottom = null, top = null;
DefaultMutableTreeNode up;
do {
up= new DefaultMutableTreeNode( clas.toString() );
if ( top != null )
up.add( top );
else
bottom = up;
top = up;
} while ( (clas = clas.getSuperclass()) != null );
tree.setModel( new DefaultTreeModel(top) );
TreeNode tn = bottom.getParent();
if ( tn != null ) {
TreePath tp = new TreePath (
((DefaultTreeModel)tree.getModel()).getPathToRoot( tn ) );
tree.expandPath( tp );
}
}
JPanel labeledPane( JComponent comp, String label ) {
JPanel jp = new JPanel( new BorderLayout() );
jp.add( "Center", comp );
jp.add( "North", new JLabel(label, SwingConstants.CENTER) );
return jp;
}
public void init() throws ClassPathException
{
// Currently we have to cast because BshClassPath is not known by
// the core.
classPath = ((ClassManagerImpl)classManager).getClassPath();
// maybe add MappingFeedbackListener here... or let desktop if it has
/*
classPath.insureInitialized( null
// get feedback on mapping...
new ConsoleInterface() {
public Reader getIn() { return null; }
public PrintStream getOut() { return System.out; }
public PrintStream getErr() { return System.err; }
public void println( String s ) { System.out.println(s); }
public void print( String s ) { System.out.print(s); }
public void print( String s, Color color ) { print( s ); }
public void error( String s ) { print( s ); }
}
);
*/
classPath.addListener( this );
Set pset = classPath.getPackagesSet();
ptree = new PackageTree( pset );
ptree.addTreeSelectionListener( new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
TreePath tp = e.getPath();
Object [] oa = tp.getPath();
StringBuffer selectedPackage = new StringBuffer();
for(int i=1; i<oa.length; i++) {
selectedPackage.append( oa[i].toString() );
if ( i+1 < oa.length )
selectedPackage.append(".");
}
setClist( selectedPackage.toString() );
}
} );
classlist=new JList();
classlist.setBackground(LIGHT_BLUE);
classlist.addListSelectionListener(this);
conslist = new JList();
conslist.addListSelectionListener(this);
mlist = new JList();
mlist.setBackground(LIGHT_BLUE);
mlist.addListSelectionListener(this);
fieldlist = new JList();
fieldlist.addListSelectionListener(this);
JSplitPane methodConsPane = splitPane(
JSplitPane.VERTICAL_SPLIT, true,
labeledPane(new JScrollPane(conslist), "Constructors"),
labeledPane(new JScrollPane(mlist), "Methods")
);
JSplitPane rightPane = splitPane(JSplitPane.VERTICAL_SPLIT, true,
methodConsPane,
labeledPane(new JScrollPane(fieldlist), "Fields")
);
JSplitPane sp = splitPane(
JSplitPane.HORIZONTAL_SPLIT, true,
labeledPane(new JScrollPane(classlist), "Classes"),
rightPane );
sp = splitPane(
JSplitPane.HORIZONTAL_SPLIT, true,
labeledPane(new JScrollPane(ptree), "Packages"), sp);
JPanel bottompanel = new JPanel( new BorderLayout() );
methodLine = new JTextArea(1,60);
methodLine.setBackground(LIGHT_BLUE);
methodLine.setEditable(false);
methodLine.setLineWrap(true);
methodLine.setWrapStyleWord(true);
methodLine.setFont( new Font("Monospaced", Font.BOLD, 14) );
methodLine.setMargin( new Insets(5,5,5,5) );
methodLine.setBorder( new MatteBorder(1,0,1,0,
LIGHT_BLUE.darker().darker()) );
bottompanel.add("North", methodLine);
JPanel p = new JPanel( new BorderLayout() );
tree = new JTree();
tree.addTreeSelectionListener( new TreeSelectionListener() {
public void valueChanged(TreeSelectionEvent e) {
driveToClass( e.getPath().getLastPathComponent().toString() );
}
} );
tree.setBorder( BorderFactory.createRaisedBevelBorder() );
setClassTree(null);
p.add( "Center", tree );
bottompanel.add("Center", p );
// give it a preferred height
bottompanel.setPreferredSize(new java.awt.Dimension(150,150));
setTopComponent( sp );
setBottomComponent( bottompanel );
}
private JSplitPane splitPane(
int orientation,
boolean redraw,
JComponent c1,
JComponent c2
) {
JSplitPane sp = new JSplitPane(orientation, redraw, c1, c2);
sp.setBorder(null);
javax.swing.plaf.SplitPaneUI ui = sp.getUI();
if(ui instanceof javax.swing.plaf.basic.BasicSplitPaneUI) {
((javax.swing.plaf.basic.BasicSplitPaneUI)ui).getDivider()
.setBorder(null);
}
return sp;
}
public static void main( String [] args )
throws Exception
{
ClassBrowser cb = new ClassBrowser();
cb.init();
JFrame f=new JFrame("BeanShell Class Browser v1.0");
f.getContentPane().add( "Center", cb );
cb.setFrame( f );
f.pack();
f.setVisible(true);
}
public void setFrame( JFrame frame ) {
this.frame = frame;
}
public void setFrame( JInternalFrame frame ) {
this.iframe = frame;
}
public void valueChanged(ListSelectionEvent e)
{
if ( e.getSource() == classlist )
{
String classname = (String)classlist.getSelectedValue();
setMlist( classname );
// hack
// show the class source in the "method" line...
String methodLineString;
if ( classname == null )
methodLineString = "Package: "+selectedPackage;
else
{
String fullClassName =
selectedPackage.equals("<unpackaged>") ?
classname : selectedPackage+"."+classname;
methodLineString =
fullClassName
+" (from "+ classPath.getClassSource( fullClassName ) +")";
}
setMethodLine( methodLineString );
}
else
if ( e.getSource() == mlist )
{
int i = mlist.getSelectedIndex();
if ( i == -1 )
setMethodLine( null );
else
setMethodLine( methodList[i] );
}
else
if ( e.getSource() == conslist )
{
int i = conslist.getSelectedIndex();
if ( i == -1 )
setMethodLine( null );
else
setMethodLine( consList[i] );
}
else
if ( e.getSource() == fieldlist )
{
int i = fieldlist.getSelectedIndex();
if ( i == -1 )
setMethodLine( null );
else
setMethodLine( fieldList[i] );
}
}
// fully qualified classname
public void driveToClass( String classname ) {
String [] sa = BshClassPath.splitClassname( classname );
String packn = sa[0];
String classn = sa[1];
// Do we have the package?
if ( classPath.getClassesForPackage(packn).size()==0 )
return;
ptree.setSelectedPackage( packn );
for(int i=0; i< classesList.length; i++) {
if ( classesList[i].equals(classn) ) {
classlist.setSelectedIndex(i);
classlist.ensureIndexIsVisible(i);
break;
}
}
}
public void toFront() {
if ( frame != null )
frame.toFront();
else
if ( iframe != null )
iframe.toFront();
}
class PackageTree extends JTree
{
TreeNode root;
DefaultTreeModel treeModel;
Map nodeForPackage = new HashMap();
PackageTree( Collection packages ) {
setPackages( packages );
setRootVisible(false);
setShowsRootHandles(true);
setExpandsSelectedPaths(true);
// open top level paths
/*
Enumeration e1=root.children();
while( e1.hasMoreElements() ) {
TreePath tp = new TreePath(
treeModel.getPathToRoot( (TreeNode)e1.nextElement() ) );
expandPath( tp );
}
*/
}
public void setPackages( Collection packages ) {
treeModel = makeTreeModel(packages);
setModel( treeModel );
}
DefaultTreeModel makeTreeModel( Collection packages )
{
Map packageTree = new HashMap();
Iterator it=packages.iterator();
while( it.hasNext() ) {
String pack = (String)(it.next());
String [] sa = StringUtil.split( pack, "." );
Map level=packageTree;
for (int i=0; i< sa.length; i++ ) {
String name = sa[i];
Map map=(Map)level.get( name );
if ( map == null ) {
map=new HashMap();
level.put( name, map );
}
level = map;
}
}
root = makeNode( packageTree, "root" );
mapNodes(root);
return new DefaultTreeModel( root );
}
MutableTreeNode makeNode( Map map, String nodeName )
{
DefaultMutableTreeNode root =
new DefaultMutableTreeNode( nodeName );
Iterator it=map.keySet().iterator();
while(it.hasNext() ) {
String name = (String)it.next();
Map val = (Map)map.get(name);
if ( val.size() == 0 ) {
DefaultMutableTreeNode leaf =
new DefaultMutableTreeNode( name );
root.add( leaf );
} else {
MutableTreeNode node = makeNode( val, name );
root.add( node );
}
}
return root;
}
/**
Map out the location of the nodes by package name.
Seems like we should be able to do this while we build above...
I'm tired... just going to do this.
*/
void mapNodes( TreeNode node ) {
addNodeMap( node );
Enumeration e = node.children();
while(e.hasMoreElements()) {
TreeNode tn = (TreeNode)e.nextElement();
mapNodes( tn );
}
}
/**
map a single node up to the root
*/
void addNodeMap( TreeNode node ) {
StringBuffer sb = new StringBuffer();
TreeNode tn = node;
while( tn != root ) {
sb.insert(0, tn.toString() );
if ( tn.getParent() != root )
sb.insert(0, "." );
tn = tn.getParent();
}
String pack = sb.toString();
nodeForPackage.put( pack, node );
}
void setSelectedPackage( String pack ) {
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)nodeForPackage.get(pack);
if ( node == null )
return;
TreePath tp = new TreePath(treeModel.getPathToRoot( node ));
setSelectionPath( tp );
setClist( pack );
scrollPathToVisible( tp );
}
}
public void classPathChanged() {
Set pset = classPath.getPackagesSet();
ptree.setPackages( pset );
setClist(null);
}
}
|