/*****************************************************************************
* *
* 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 (pat@pat.net) *
* 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);
}
}