Coverage Report - net.wotonomy.ui.swing.util.StackTraceInspector
 
Classes in this File Line Coverage Branch Coverage Complexity
StackTraceInspector
0% 
0% 
2.231
 
 1  
 /*
 2  
 Wotonomy: OpenStep design patterns for pure Java applications.
 3  
 Copyright (C) 2000 Michael Powers
 4  
 
 5  
 This library is free software; you can redistribute it and/or
 6  
 modify it under the terms of the GNU Lesser General Public
 7  
 License as published by the Free Software Foundation; either
 8  
 version 2.1 of the License, or (at your option) any later version.
 9  
 
 10  
 This library is distributed in the hope that it will be useful,
 11  
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
 Lesser General Public License for more details.
 14  
 
 15  
 You should have received a copy of the GNU Lesser General Public
 16  
 License along with this library; if not, see http://www.gnu.org
 17  
 */
 18  
 
 19  
 package net.wotonomy.ui.swing.util;
 20  
 
 21  
 import java.awt.BorderLayout;
 22  
 import java.awt.Dimension;
 23  
 import java.awt.Insets;
 24  
 import java.awt.Toolkit;
 25  
 import java.awt.datatransfer.Clipboard;
 26  
 import java.awt.datatransfer.StringSelection;
 27  
 import java.awt.event.ActionEvent;
 28  
 import java.awt.event.ActionListener;
 29  
 import java.awt.event.KeyEvent;
 30  
 import java.awt.event.MouseEvent;
 31  
 import java.awt.event.MouseListener;
 32  
 import java.io.ByteArrayOutputStream;
 33  
 import java.io.File;
 34  
 import java.io.PrintStream;
 35  
 import java.util.ArrayList;
 36  
 import java.util.Iterator;
 37  
 import java.util.List;
 38  
 import java.util.StringTokenizer;
 39  
 
 40  
 import javax.swing.JComponent;
 41  
 import javax.swing.JFrame;
 42  
 import javax.swing.JPanel;
 43  
 import javax.swing.JScrollPane;
 44  
 import javax.swing.JTable;
 45  
 import javax.swing.KeyStroke;
 46  
 import javax.swing.border.EmptyBorder;
 47  
 import javax.swing.event.TableModelListener;
 48  
 import javax.swing.table.TableModel;
 49  
 
 50  
 import net.wotonomy.ui.swing.components.MultiLineLabel;
 51  
 
 52  
 /**
 53  
 * The StackTraceInspector displays a JFrame containing
 54  
 * stack trace information for a Throwable.  <br><br>
 55  
 *
 56  
 * There are also a few static methods for obtaining
 57  
 * information about the current stack, which is useful
 58  
 * for determining who's calling you at runtime.
 59  
 *
 60  
 * @author michael@mpowers.net
 61  
 * @version $Revision: 904 $
 62  
 */
 63  
 
 64  
 public class StackTraceInspector
 65  
     implements TableModel, MouseListener, ActionListener
 66  
 {
 67  0
     protected JTable table = null;
 68  0
     protected List tableModelListeners = null;
 69  0
     protected List methodNames = new ArrayList();
 70  
 
 71  
     // key command to copy contents to clipboard
 72  
     static public final String COPY = "COPY";
 73  
 
 74  
 /**
 75  
 * Displays the current stack trace at the time
 76  
 * of instantiation in a table on a frame.
 77  
 */
 78  0
     public StackTraceInspector()
 79  0
     {
 80  0
         initLayout( parseStackTrace( new RuntimeException() ), null );
 81  0
     }
 82  
 
 83  
 /**
 84  
 * Displays the current stack trace at the time
 85  
 * of instantiation in a table on a frame
 86  
 * annotated with the specified message.
 87  
 */
 88  0
     public StackTraceInspector( String aMessage )
 89  0
     {
 90  0
         initLayout( parseStackTrace( new RuntimeException() ), aMessage );
 91  0
     }
 92  
 
 93  
 /**
 94  
 * Displays the stack trace for the given throwable
 95  
 * in a table on a frame.
 96  
 * @param aThrowable A Throwable whose stack will be examined.
 97  
 */
 98  0
     public StackTraceInspector( Throwable aThrowable )
 99  0
     {
 100  0
         initLayout( parseStackTrace( aThrowable ),
 101  0
                         aThrowable.getClass() + ": " + aThrowable.getMessage() );
 102  0
     }
 103  
 
 104  
 /**
 105  
 * Simply displays the list items in a dialog.
 106  
 * Presumably (but not necessarily) called from
 107  
 * the other constructors.  (I guess if you just
 108  
 * want a frame with strings in table, you can
 109  
 * call this.)
 110  
 * @param aStringList A List containing Strings.
 111  
 */
 112  0
     public StackTraceInspector( List aStringList )
 113  0
     {
 114  0
         initLayout( aStringList, null );
 115  0
     }
 116  
 
 117  
     protected void initLayout( List items, String message )
 118  
     {
 119  0
         methodNames = new ArrayList( items );
 120  0
         table = new JTable( this ); // this class is the table model
 121  0
         table.addMouseListener( this ); // listen for double-clicks
 122  
 
 123  
         // set up keyboard events for cut-copy: Ctrl-C, Ctrl-X
 124  0
         table.registerKeyboardAction( this, COPY,
 125  0
             KeyStroke.getKeyStroke( KeyEvent.VK_C, 
 126  0
             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
 127  0
             JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
 128  0
         table.registerKeyboardAction( this, COPY,
 129  0
             KeyStroke.getKeyStroke( KeyEvent.VK_X, 
 130  0
             Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() ),
 131  0
             JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT );
 132  
 
 133  0
         JPanel panel = new JPanel();
 134  0
         panel.setBorder( new EmptyBorder( new Insets( 10, 10, 10, 10 ) ) );
 135  0
         panel.setLayout( new BorderLayout( 10, 10 ) );
 136  
                 
 137  0
                 if ( message != null )
 138  
                 {
 139  0
                         panel.add( new MultiLineLabel( message ), BorderLayout.NORTH );        
 140  
                 }
 141  
 
 142  0
         JScrollPane scrollPane = new JScrollPane( table );
 143  0
         scrollPane.setPreferredSize( new Dimension( 325, 350 ) );
 144  0
         panel.add( scrollPane, BorderLayout.CENTER );
 145  
 
 146  0
         JFrame window = new JFrame();
 147  0
         window.setTitle( "Stack Trace Inspector" );
 148  0
         window.getContentPane().add( panel );
 149  
 
 150  0
         window.pack();
 151  0
         WindowUtilities.cascade( window );
 152  0
         window.show();
 153  0
     }
 154  
 
 155  
     // interface TableModel
 156  
 
 157  
     public int getRowCount()
 158  
     {
 159  0
         return methodNames.size();
 160  
     }
 161  
 
 162  
     public int getColumnCount()
 163  
     {
 164  0
         return 1;
 165  
     }
 166  
 
 167  
     public String getColumnName(int columnIndex)
 168  
     {
 169  0
         switch ( columnIndex )
 170  
         {
 171  
             case 0:
 172  0
                 return "Methods";
 173  
             case 1:
 174  0
                 return "Property";
 175  
         }
 176  0
         System.out.println( "StackTraceInspector.getColumnName: unknown column: " + columnIndex );
 177  0
         return "";
 178  
     }
 179  
 
 180  
     public Class getColumnClass(int columnIndex)
 181  
     {
 182  0
         switch ( columnIndex )
 183  
         {
 184  
             case 0:
 185  0
                 return String.class;
 186  
             case 1:
 187  0
                 return String.class;
 188  
         }
 189  0
         System.out.println( "StackTraceInspector.getColumnClass: unknown column: " + columnIndex );
 190  0
         return Object.class;
 191  
     }
 192  
 
 193  
     public boolean isCellEditable(int rowIndex,
 194  
                               int columnIndex)
 195  
     {
 196  0
         return false;
 197  
     }
 198  
 
 199  
     public Object getValueAt(int rowIndex,
 200  
                          int columnIndex)
 201  
     {
 202  0
         return methodNames.get( rowIndex );
 203  
     }
 204  
 
 205  
     public void setValueAt(Object aValue,
 206  
                        int rowIndex,
 207  
                        int columnIndex)
 208  
     {
 209  0
     }
 210  
 
 211  
     public void addTableModelListener(TableModelListener l)
 212  
     {
 213  0
         if ( tableModelListeners == null )
 214  
         {
 215  0
             tableModelListeners = new ArrayList();
 216  
         }
 217  0
         tableModelListeners.add( l );
 218  0
     }
 219  
 
 220  
     public void removeTableModelListener(TableModelListener l)
 221  
     {
 222  0
         if ( tableModelListeners != null )
 223  
         {
 224  0
             tableModelListeners.remove( l );
 225  
         }
 226  0
     }
 227  
 
 228  
     // interface MouseListener
 229  
 
 230  
     /**
 231  
     * Double click to call invokeFileFromString.
 232  
     */
 233  
 
 234  
     public void mouseClicked(MouseEvent e)
 235  
     {
 236  0
         if ( e.getSource() == table )
 237  
         {
 238  0
             if ( e.getClickCount() > 1 )
 239  
             {
 240  0
                 int row = table.rowAtPoint( e.getPoint() );
 241  0
                 int col = table.columnAtPoint( e.getPoint() );
 242  
 
 243  0
                 if ( ( row == -1 ) || ( col != 0 ) ) return;
 244  
 
 245  0
                 invokeFileFromString( methodNames.get( row ).toString() );
 246  
             }
 247  
         }
 248  0
     }
 249  
 
 250  0
     public void mouseReleased(MouseEvent e) {}
 251  0
     public void mousePressed(MouseEvent e) {}
 252  0
     public void mouseEntered(MouseEvent e) {}
 253  0
     public void mouseExited(MouseEvent e) {}
 254  
 
 255  
 
 256  
     // interface ActionEventListener - for listening to key commands
 257  
 
 258  
     public void actionPerformed(ActionEvent evt)
 259  
     {
 260  0
         if ( COPY.equals( evt.getActionCommand() ) )
 261  
         {
 262  0
             copyToClipboard();
 263  0
             return;
 264  
         }
 265  0
     }
 266  
 
 267  
 /**
 268  
 * Copies the contents of the table to the clipboard as a tab-delimited string.
 269  
 */
 270  
     public void copyToClipboard()
 271  
     {
 272  0
         Toolkit toolkit = Toolkit.getDefaultToolkit();
 273  0
         Clipboard clipboard = toolkit.getSystemClipboard();
 274  0
         StringSelection selection =
 275  0
             new StringSelection( getSelectedStackString() );
 276  0
         clipboard.setContents( selection, selection );
 277  0
     }
 278  
 
 279  
 /**
 280  
 * Converts the selected contents of the table to a string.
 281  
 * @return A String containing the text contents of the table.
 282  
 */
 283  
     public String getSelectedStackString()
 284  
     {
 285  0
         StringBuffer result = new StringBuffer(64);
 286  
 
 287  0
         TableModel model = table.getModel();
 288  
 
 289  
         Object o;
 290  0
         int[] selectedRows = table.getSelectedRows();
 291  0
         for ( int i = 0; i < selectedRows.length; i++ )
 292  
         {
 293  0
             o = model.getValueAt( selectedRows[i], 0 );
 294  0
             if ( o == null ) o = "";
 295  0
             result.append( o );
 296  0
             result.append( '\n' );
 297  
         }
 298  
 
 299  0
         return result.toString();
 300  
     }
 301  
 
 302  
 
 303  
     // static methods
 304  
 
 305  
 /**
 306  
 * Obtains a list of strings representing the stack trace
 307  
 * associated with this throwable starting with the most recent call.
 308  
 * @param aThrowable A Throwable whose stack trace is parsed.
 309  
 * @return a List containing the method names as Strings.
 310  
 */
 311  
     static public List parseStackTrace( Throwable aThrowable )
 312  
     {
 313  0
         String trace = null;
 314  
 
 315  
         // create new stream
 316  0
         ByteArrayOutputStream os = new ByteArrayOutputStream( 256 );
 317  0
         PrintStream newErr = new PrintStream( os );
 318  0
         aThrowable.printStackTrace( newErr ); // prints to System.err
 319  
 
 320  
         // convert to string
 321  0
         trace = os.toString();
 322  
 
 323  0
         List result = new ArrayList();
 324  
 
 325  
         // populate list with parsed trace, starting from top
 326  
         String token;
 327  0
         StringTokenizer tokens = new StringTokenizer( trace, "\n" );
 328  0
         tokens.nextToken(); // strip off description of throwable
 329  0
         while ( tokens.hasMoreTokens() )
 330  
         {
 331  0
             token = tokens.nextToken();
 332  0
             if ( token.indexOf( StackTraceInspector.class.getName() ) == -1 )
 333  
             { // add only those methods not from this class
 334  
 
 335  
                 // strip whitespace, "at  " from front, and \r from end
 336  0
                 token.trim();
 337  0
                 token = token.substring( 4, token.length() - 1 );
 338  
 
 339  0
                 result.add( token );
 340  0
             }
 341  
         }
 342  
 
 343  0
         return result;
 344  
     }
 345  
 
 346  
 /**
 347  
 * Convenience method that obtains a String representing
 348  
 * the caller's caller.
 349  
 * @return a String representing a method in stack trace format.
 350  
 */
 351  
     static public String getMyCaller()
 352  
     {
 353  0
         List trace = parseStackTrace( new RuntimeException() );
 354  0
         if ( trace.size() > 1 )
 355  
         {
 356  0
             return trace.get( 1 ).toString();
 357  
         }
 358  
 
 359  0
         return null;
 360  
     }
 361  
 
 362  
 /**
 363  
 * Prints a stack trace up to the first method whose fully
 364  
 * qualified class name begins with "java" to System.out.
 365  
 */
 366  
     static public void printShortStackTrace()
 367  
     {
 368  
         String s;
 369  0
         Iterator i = parseStackTrace( new RuntimeException() ).iterator();
 370  0
         while ( i.hasNext() )
 371  
         {
 372  0
             System.out.println( "  " + ( s = i.next().toString() ) );
 373  0
             if ( s.startsWith( "java" ) ) break;
 374  
         }
 375  0
     }
 376  
 
 377  
     protected void invokeFileFromString( String aString )
 378  
     {
 379  
         // strip off parentheses, if any
 380  0
         int openParam = aString.indexOf( "(" );
 381  0
         if ( openParam != -1 )
 382  
         {
 383  0
             aString = aString.substring( 0, openParam );
 384  
         }
 385  
 
 386  
         // separate class name from method name
 387  0
         int lastDot = aString.lastIndexOf( "." );
 388  0
         if ( lastDot == -1 ) return;
 389  0
         String className = aString.substring( 0, lastDot );
 390  0
         String methodName = aString.substring( lastDot + 1 );
 391  
 
 392  
         // convert "."s to file separator characters
 393  0
         StringBuffer buf = new StringBuffer();
 394  0
         StringTokenizer tokens = new StringTokenizer( className, "." );
 395  0
         while ( true )
 396  
         {
 397  0
             buf.append( tokens.nextToken() );
 398  0
             if ( ! tokens.hasMoreTokens() ) break;
 399  0
             buf.append( File.separator );
 400  0
         }
 401  0
         String path = buf.toString();
 402  0
         java.net.URL url = ClassLoader.getSystemResource( path + ".java" );
 403  0
         if ( url == null ) return; // do nothing
 404  
 
 405  0
         String name = url.getFile();
 406  
 
 407  
         // try to launch the document
 408  
         try
 409  
         {
 410  
             // NOTE: This is Windows-dependent!
 411  0
             String args[] = new String[] {
 412  0
                 "cmd", "/c", "\"start \"\" \"" + name.substring(1) + "\"\"" };
 413  
                 // this translates to: cmd /c "start "" "path""
 414  
                 // apparently an array is more reliable for calling exec().
 415  
                 // all the extra quotes are to handle paths with spaces.
 416  
                 // trims off the first "/" before the drive letter.
 417  
                 // needed a dummy title for the console or it wouldn't work.
 418  0
             Runtime.getRuntime().exec( args );
 419  
         }
 420  0
         catch ( Exception exc )
 421  
         {
 422  0
             System.out.println( "DocumentLinkPanel.invokeDocument: " + exc );
 423  0
         }
 424  0
         return;
 425  
     }
 426  
 
 427  
 }
 428  
 
 429  
 /*
 430  
  * $Log$
 431  
  * Revision 1.2  2006/02/18 23:19:05  cgruber
 432  
  * Update imports and maven dependencies.
 433  
  *
 434  
  * Revision 1.1  2006/02/16 13:22:22  cgruber
 435  
  * Check in all sources in eclipse-friendly maven-enabled packages.
 436  
  *
 437  
  * Revision 1.5  2003/08/06 23:07:53  chochos
 438  
  * general code cleanup (mostly, removing unused imports)
 439  
  *
 440  
  * Revision 1.4  2002/11/16 16:33:31  mpowers
 441  
  * Now using platform-specific accelerator key for shortcuts.
 442  
  *
 443  
  * Revision 1.3  2001/07/18 21:53:33  mpowers
 444  
  * Added a string argument for display as a message.
 445  
  *
 446  
  * Revision 1.2  2001/07/17 14:01:43  mpowers
 447  
  * Added short stack trace method.
 448  
  *
 449  
  * Revision 1.1.1.1  2000/12/21 15:51:34  mpowers
 450  
  * Contributing wotonomy.
 451  
  *
 452  
  * Revision 1.5  2000/12/20 16:25:45  michael
 453  
  * Added log to all files.
 454  
  *
 455  
  *
 456  
  */
 457