Open Source Repository

Home /beanshell/bsh-2.0b4 | Repository Home


bsh/util/ClassBrowser.java
/*****************************************************************************
 *                                                                           *
 *  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() {
    thisBshClassManager.createClassManagernull/*interpreter*/ ) );
  }

  public ClassBrowserBshClassManager classManager ) {
    superVERTICAL_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);
    String [] sa = (String[])(l.toArraynew String[0] ));
    return StringUtil.bubbleSort(sa);
  }

  void setClistString packagename ) {
    this.selectedPackage = packagename;

    Set set = classPath.getClassesForPackagepackagename );
    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("$"== -)
        list.addBshClassPath.splitClassnamecname )[1] );
    }

    classesList = toSortedStrings(list);
    classlist.setListDataclassesList );
    //setMlist( (String)classlist.getModel().getElementAt(0) );
  }

  String [] parseConstructorsConstructor [] 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 [] parseMethodsMethod [] 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 [] parseFieldsField[] 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 [] getPublicConstructorsConstructor [] constructors ) {
    Vector v = new Vector();
    for(int i=0; i< constructors.length; i++)
      if Modifier.isPublic(constructors[i].getModifiers()) )
        v.addElementconstructors[i] );

    Constructor [] ca = new Constructor v.size() ];
    v.copyIntoca );
    return ca;
  }
  
  Method [] getPublicMethodsMethod [] methods ) {
    Vector v = new Vector();
    for(int i=0; i< methods.length; i++)
      if Modifier.isPublic(methods[i].getModifiers()) )
        v.addElementmethods[i] );

    Method [] ma = new Method v.size() ];
    v.copyIntoma );
    return ma;
  }
  
  Field[] getPublicFieldsField [] fields ) {
    Vector v = new Vector();
    for(int i=0; i< fields.length; i++)
      if Modifier.isPublic(fields[i].getModifiers()) )
        v.addElementfields[i] );

    Field [] fa = new Field v.size() ];
    v.copyIntofa );
    return fa;    
  }

  void setConslistClass clas ) {
    if clas == null ) {
      conslist.setListDatanew Object [] { } );
      return;
    }

    consList = getPublicConstructorsclas.getDeclaredConstructors() );
    conslist.setListDataparseConstructors(consList) );
  }  
  
  void setMlistString classname 
  {
    if classname == null 
    {
      mlist.setListDatanew Object [] { } );
      setConslistnull );
      setClassTreenull );
      return;
    }

    Class clas;
    try {
      if selectedPackage.equals("<unpackaged>") )
        selectedClass = classManager.classForNameclassname );
      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 = getPublicMethodsselectedClass.getDeclaredMethods() );
    mlist.setListDataparseMethods(methodList) );
    
    setClassTreeselectedClass );
    setConslistselectedClass );
    setFieldListselectedClass );
  }
  
  void setFieldListClass clas ) {
    if clas == null ) {
      fieldlist.setListDatanew Object [] { } );
      return;
    }

    fieldList = getPublicFields(clas.getDeclaredFields());
    fieldlist.setListDataparseFields(fieldList) );
  }    

  void setMethodLineObject method ) {
    methodLine.setTextmethod==null "" : method.toString() );
  }

  void setClassTreeClass clas ) {
    if clas == null ) {
      tree.setModelnull );
      return;
    }
      
    MutableTreeNode bottom = null, top = null;
    DefaultMutableTreeNode up;
    do {
      up= new DefaultMutableTreeNodeclas.toString() );
      if top != null )
        up.addtop );
      else
        bottom = up;
      top = up;
    while ( (clas = clas.getSuperclass()) != null );
    tree.setModelnew DefaultTreeModel(top) );

    TreeNode tn = bottom.getParent();
    if tn != null ) {
      TreePath tp =  new TreePath (
        ((DefaultTreeModel)tree.getModel()).getPathToRoottn ) );
      tree.expandPathtp );
    }
  }

  JPanel labeledPaneJComponent comp, String label ) {
    JPanel jp = new JPanelnew 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.addListenerthis );

    Set pset = classPath.getPackagesSet();

    ptree = new PackageTreepset );
    ptree.addTreeSelectionListenernew 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.appendoa[i].toString() );
          if i+< oa.length )
            selectedPackage.append(".");
        }
        setClistselectedPackage.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 JPanelnew BorderLayout() );
    methodLine = new JTextArea(1,60);
    methodLine.setBackground(LIGHT_BLUE);
    methodLine.setEditable(false);
    methodLine.setLineWrap(true);
    methodLine.setWrapStyleWord(true);
    methodLine.setFontnew Font("Monospaced", Font.BOLD, 14) );
    methodLine.setMarginnew Insets(5,5,5,5) );
    methodLine.setBordernew MatteBorder(1,0,1,0,
      LIGHT_BLUE.darker().darker()) );
    bottompanel.add("North", methodLine);
    JPanel p = new JPanelnew BorderLayout() );

    tree = new JTree();
    tree.addTreeSelectionListenernew TreeSelectionListener() {
      public void valueChanged(TreeSelectionEvent e) {
        driveToClasse.getPath().getLastPathComponent().toString() );
      }
    } );

    tree.setBorderBorderFactory.createRaisedBevelBorder() );
    setClassTree(null);
    p.add"Center", tree );
    bottompanel.add("Center", p );

    // give it a preferred height
    bottompanel.setPreferredSize(new java.awt.Dimension(150,150));
    
    setTopComponentsp );
    setBottomComponentbottompanel );
  }
  
  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 mainString [] 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.pack();
    f.setVisible(true);
  }

  public void setFrameJFrame frame ) {
    this.frame = frame;
  }
  public void setFrameJInternalFrame frame ) {
    this.iframe = frame;
  }

  public void valueChanged(ListSelectionEvent e
  {
    if e.getSource() == classlist 
    {
      String classname = (String)classlist.getSelectedValue();
      setMlistclassname );

      // 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.getClassSourcefullClassName +")";
      }

      setMethodLinemethodLineString );
    
    else 
    if e.getSource() == mlist 
    {
      int i = mlist.getSelectedIndex();
      if i == -)
        setMethodLinenull );
      else
        setMethodLinemethodList[i] );
    
    else
    if e.getSource() == conslist 
    {
      int i = conslist.getSelectedIndex();
      if i == -)
        setMethodLinenull );
      else
        setMethodLineconsList[i] );
    }
    else
    if e.getSource() == fieldlist 
    {
      int i = fieldlist.getSelectedIndex();
      if i == -)
        setMethodLinenull );
      else
        setMethodLinefieldList[i] );
    
  }

  // fully qualified classname
  public void driveToClassString classname ) {
    String [] sa = BshClassPath.splitClassnameclassname );
    String packn = sa[0];
    String classn = sa[1];

    // Do we have the package?
    if classPath.getClassesForPackage(packn).size()==)
      return;

    ptree.setSelectedPackagepackn );

    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();

    PackageTreeCollection packages ) {
      setPackagespackages );

      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 setPackagesCollection packages ) {
      treeModel = makeTreeModel(packages);
      setModeltreeModel );
    }
    
    DefaultTreeModel makeTreeModelCollection packages 
    {
      Map packageTree = new HashMap();

      Iterator it=packages.iterator();
      whileit.hasNext() ) {
        String pack = (String)(it.next());
        String [] sa = StringUtil.splitpack, "." );
        Map level=packageTree;
        for (int i=0; i< sa.length; i++ ) {
          String name = sa[i];
          Map map=(Map)level.getname );

          if map == null ) {
            map=new HashMap();
            level.putname, map );
          
          level = map;
        }
      }

      root = makeNodepackageTree, "root" );
      mapNodes(root);
      return new DefaultTreeModelroot );
    }


    MutableTreeNode makeNodeMap map, String nodeName 
    {
      DefaultMutableTreeNode root = 
        new DefaultMutableTreeNodenodeName );
      Iterator it=map.keySet().iterator();
      while(it.hasNext() ) {
        String name = (String)it.next();
        Map val = (Map)map.get(name);
        if val.size() == ) {
          DefaultMutableTreeNode leaf = 
            new DefaultMutableTreeNodename );
          root.addleaf );
        else {
          MutableTreeNode node = makeNodeval, name );
          root.addnode );
        }
      }
      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 mapNodesTreeNode node ) {
      addNodeMapnode );

      Enumeration e = node.children();
      while(e.hasMoreElements()) {
        TreeNode tn = (TreeNode)e.nextElement();
        mapNodestn );
      }
    }

    /**
      map a single node up to the root
    */
    void addNodeMapTreeNode node ) {

      StringBuffer sb = new StringBuffer();
      TreeNode tn = node;
      whiletn != root ) {
        sb.insert(0, tn.toString() );
        if tn.getParent() != root )
          sb.insert(0"." );
        tn = tn.getParent();
      }
      String pack = sb.toString();

      nodeForPackage.putpack, node );
    }

    void setSelectedPackageString pack ) {
      DefaultMutableTreeNode node = 
        (DefaultMutableTreeNode)nodeForPackage.get(pack);
      if node == null )
        return;

      TreePath tp = new TreePath(treeModel.getPathToRootnode ));
      setSelectionPathtp );
      setClistpack );

      scrollPathToVisibletp );
    }

  }

  public void classPathChanged() {
    Set pset = classPath.getPackagesSet();
    ptree.setPackagespset );
    setClist(null);
  }

}