Clover coverage report - JFCUnit Test Coverage
Coverage timestamp: Mon Dec 20 2004 23:38:10 MST
file stats: LOC: 368   Methods: 20
NCLOC: 170   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
ComponentBrowser.java 0% 0% 0% 0%
coverage
 1   
 package junit.extensions.jfcunit.tools;
 2   
 
 3   
 import java.awt.BorderLayout;
 4   
 import java.awt.Component;
 5   
 import java.awt.Container;
 6   
 import java.awt.Dimension;
 7   
 import java.awt.event.ActionEvent;
 8   
 import java.awt.event.ActionListener;
 9   
 import java.awt.event.WindowEvent;
 10   
 import java.awt.event.WindowListener;
 11   
 
 12   
 import java.lang.reflect.InvocationTargetException;
 13   
 import java.lang.reflect.Method;
 14   
 
 15   
 import java.util.Vector;
 16   
 
 17   
 import javax.swing.JButton;
 18   
 import javax.swing.JFrame;
 19   
 import javax.swing.JPanel;
 20   
 import javax.swing.JScrollPane;
 21   
 import javax.swing.JSplitPane;
 22   
 import javax.swing.JTable;
 23   
 import javax.swing.JTree;
 24   
 import javax.swing.event.TreeSelectionEvent;
 25   
 import javax.swing.event.TreeSelectionListener;
 26   
 import javax.swing.table.DefaultTableModel;
 27   
 import javax.swing.tree.DefaultTreeModel;
 28   
 
 29   
 
 30   
 /**
 31   
  * Component Browser utility.
 32   
  * Displays the components of a Java application in a component
 33   
  * tree. When objects are selected, there properties are displayed
 34   
  * in the property table.
 35   
  *
 36   
  * @author <a href="mailto:vraravam@thoughtworks.com">Vijay Aravamudhan : ThoughtWorks Inc.</a>
 37   
  */
 38   
 public final class ComponentBrowser extends JFrame implements ActionListener,
 39   
     TreeSelectionListener,
 40   
     WindowListener {
 41   
     /**
 42   
      * Title of the Component Browser Window.
 43   
      */
 44   
     protected static final String TITLE = "ComponentBrowser";
 45   
 
 46   
     /**
 47   
      * Property Table Model.
 48   
      */
 49   
     private DefaultTableModel m_propTableModel;
 50   
 
 51   
     /**
 52   
      * Refresh Button.
 53   
      */
 54   
     private JButton m_refreshButton;
 55   
 
 56   
     /**
 57   
      * Component Tree.
 58   
      */
 59   
     private JTree m_componentTree;
 60   
 
 61   
     /**
 62   
      * Default constructor.
 63   
      */
 64  0
     public ComponentBrowser() {
 65  0
         setTitle(TITLE);
 66  0
         equip(getContentPane());
 67  0
         setSize(400, 300);
 68  0
         pack();
 69  0
         setVisible(true);
 70  0
         addWindowListener(this);
 71   
     }
 72   
 
 73   
     /**
 74   
      * Just another dumb main method.
 75   
      *
 76   
      * @param args Command line arguments.
 77   
      */
 78  0
     public static void main(final String[] args) {
 79  0
         new ComponentBrowser();
 80   
     }
 81   
 
 82   
     /**
 83   
      * Utility method showing whether a component
 84   
      * node has been selected or not.
 85   
      *
 86   
      * @return boolean True if a component is selected in the tree.
 87   
      */
 88  0
     public boolean isComponentSelected() {
 89  0
         return ((ComponentNode) m_componentTree.getLastSelectedPathComponent() != null);
 90   
     }
 91   
 
 92   
     /**
 93   
      * Invoked when an action occurs.
 94   
      *
 95   
      * @param e dispatched action event.
 96   
      */
 97  0
     public void actionPerformed(final ActionEvent e) {
 98  0
         if (e.getSource() == m_refreshButton) {
 99  0
             ((DefaultTreeModel) m_componentTree.getModel()).reload();
 100   
         }
 101   
     }
 102   
 
 103   
     /**
 104   
      * Called whenever the value of the selection changes.
 105   
      *
 106   
      * @param e the event that characterizes the change.
 107   
      */
 108  0
     public void valueChanged(final TreeSelectionEvent e) {
 109  0
         if (e.getSource() == m_componentTree) {
 110  0
             if (isComponentSelected()) {
 111  0
                 ComponentNode node = (ComponentNode) m_componentTree
 112   
                     .getLastSelectedPathComponent();
 113  0
                 componentSelected(node.getComponent());
 114   
             }
 115   
         }
 116   
     }
 117   
 
 118   
     /**
 119   
      * Invoked when the window is set to be the user's
 120   
      * active window, which means the window (or one of its
 121   
      * subcomponents) will receive keyboard events.
 122   
      *
 123   
      * @param e dispatched window event.
 124   
      */
 125  0
     public void windowActivated(final WindowEvent e) {
 126   
     }
 127   
 
 128   
     /**
 129   
      * Invoked when a window has been closed as the result
 130   
      * of calling dispose on the window.
 131   
      *
 132   
      * @param e dispatched window event.
 133   
      */
 134  0
     public void windowClosed(final WindowEvent e) {
 135   
     }
 136   
 
 137   
     /**
 138   
      * Invoked when the user attempts to close the window
 139   
      * from the window's system menu.  If the program does not
 140   
      * explicitly hide or dispose the window while processing
 141   
      * this event, the window close operation will be cancelled.
 142   
      *
 143   
      * @param e dispatched window event.
 144   
      */
 145  0
     public void windowClosing(final WindowEvent e) {
 146  0
         System.exit(0);
 147   
     }
 148   
 
 149   
     /**
 150   
      * Invoked when a window is no longer the user's active
 151   
      * window, which means that keyboard events will no longer
 152   
      * be delivered to the window or its subcomponents.
 153   
      *
 154   
      * @param e dispatched window event.
 155   
      */
 156  0
     public void windowDeactivated(final WindowEvent e) {
 157   
     }
 158   
 
 159   
     /**
 160   
      * Invoked when a window is changed from a minimized
 161   
      * to a normal state.
 162   
      *
 163   
      * @param e dispatched window event.
 164   
      */
 165  0
     public void windowDeiconified(final WindowEvent e) {
 166   
     }
 167   
 
 168   
     /**
 169   
      * Invoked when a window is changed from a normal to a
 170   
      * minimized state. For many platforms, a minimized window
 171   
      * is displayed as the icon specified in the window's
 172   
      * iconImage property.
 173   
      *
 174   
      * @param e dispatched window event.
 175   
      */
 176  0
     public void windowIconified(final WindowEvent e) {
 177   
     }
 178   
 
 179   
     /**
 180   
      * Invoked the first time a window is made visible.
 181   
      *
 182   
      * @param e dispatched window event.
 183   
      */
 184  0
     public void windowOpened(final WindowEvent e) {
 185   
     }
 186   
 
 187   
     /**
 188   
      * Method to check if the method specified on the class
 189   
      * is a "getter" for an attribute of that class.
 190   
      *
 191   
      * @param method The method to be tested.
 192   
      * @return true if the method is a "getter".
 193   
      */
 194  0
     private boolean isAccessibleGetterMethod(final Method method) {
 195  0
         return (method.getName().startsWith("get")
 196   
         || method.getName().startsWith("is"));
 197   
     }
 198   
 
 199   
     /**
 200   
      * Method to "extract" the property name from the method name
 201   
      * following the convention specified for a bean.
 202   
      *
 203   
      * @param methodName The name of the method.
 204   
      * @return The name of the attribute.
 205   
      */
 206  0
     private String getPropertyName(final String methodName) {
 207  0
         String propName = null;
 208   
 
 209  0
         if (methodName.startsWith("get")) {
 210  0
             propName = methodName.substring(3);
 211  0
         } else if (methodName.startsWith("is")) {
 212  0
             propName = methodName.substring(2);
 213   
         }
 214   
 
 215  0
         return propName.substring(0, 1).toLowerCase() + propName.substring(1);
 216   
     }
 217   
 
 218   
     /**
 219   
      * Method to create and add rows to the table model to show
 220   
      * the various attributes of the current selected component.
 221   
      *
 222   
      * @param comp      The component selected on the JTree.
 223   
      * @param method    The method associated with an attribute.
 224   
      * @param processed The list of already processed methods - to take care of
 225   
      *                  methods in the super classes.
 226   
      */
 227  0
     private void addProperty(final Component comp, final Method method,
 228   
         final Vector processed) {
 229  0
         String name = method.getName();
 230   
 
 231  0
         if (processed.contains(name)) {
 232  0
             return;
 233   
         }
 234   
 
 235  0
         processed.addElement(name);
 236   
 
 237  0
         try {
 238  0
             Class[] paramTypes = method.getParameterTypes();
 239   
 
 240   
             // any getter method which accepts an argument
 241   
             // is not a property getter
 242  0
             if (paramTypes.length == 0) {
 243  0
                 Vector data = new Vector(2);
 244  0
                 data.addElement(getPropertyName(method.getName()));
 245  0
                 data.addElement(method.invoke(
 246   
                         comp,
 247   
                         new Class[0]));
 248  0
                 m_propTableModel.addRow(data);
 249   
             }
 250   
         } catch (IllegalArgumentException e) {
 251  0
             e.printStackTrace();
 252   
         } catch (InvocationTargetException e) {
 253   
             ; // don't do anything, but still catch it
 254   
         } catch (IllegalAccessException e) {
 255   
             ; // don't do anything, but still catch it
 256   
         }
 257   
     }
 258   
 
 259   
     /**
 260   
      * The method which does the actual work of "refreshing"
 261   
      * the right side of the main frame with the data for the
 262   
      * component selected on the JTree.
 263   
      *
 264   
      * @param comp   The selected component.
 265   
      */
 266  0
     private void componentSelected(final Component comp) {
 267   
         // The setNumRows() method is being used so as to be
 268   
         // compatible with JDK 1.2.2: should be replaced
 269   
         // with setRowCount() instead.
 270  0
         m_propTableModel.setNumRows(0); //kludge to remove all rows
 271   
 
 272  0
         if (comp == null) {
 273  0
             return;
 274   
         }
 275   
 
 276  0
         Class  superClass = comp.getClass();
 277  0
         Vector processed = new Vector();
 278   
 
 279  0
         while (Component.class.isAssignableFrom(superClass)) {
 280  0
             Method[] methods = superClass.getDeclaredMethods();
 281   
 
 282  0
             for (int i = 0; i < methods.length; i++) {
 283  0
                 if (isAccessibleGetterMethod(methods[i])) {
 284  0
                     addProperty(comp, methods[i], processed);
 285   
                 }
 286   
             }
 287   
 
 288  0
             superClass = superClass.getSuperclass();
 289   
         }
 290   
 
 291   
         /** @todo: might be a good idea to also sort the elements in 'propTableModel' */
 292  0
         m_propTableModel.fireTableDataChanged();
 293   
     }
 294   
 
 295   
     /**
 296   
      * Method to create the left pane consisting of the
 297   
      * reload button and a JTree.
 298   
      *
 299   
      * @return A JPanel for the left side of the main frame
 300   
      */
 301  0
     private JPanel createLeftPanel() {
 302  0
         m_componentTree = new JTree(new ComponentNode(null, null));
 303  0
         m_componentTree.addTreeSelectionListener(this);
 304   
 
 305  0
         JPanel leftPane = new JPanel(new BorderLayout());
 306  0
         leftPane.setSize(100, 300);
 307  0
         leftPane.setPreferredSize(leftPane.getSize());
 308  0
         leftPane.add(
 309   
             new JScrollPane(m_componentTree),
 310   
             BorderLayout.CENTER);
 311  0
         m_refreshButton = createReloadButton();
 312  0
         leftPane.add(m_refreshButton, BorderLayout.SOUTH);
 313   
 
 314  0
         return leftPane;
 315   
     }
 316   
 
 317   
     /**
 318   
      * Method to create a default button to reload the JTree.
 319   
      *
 320   
      * @return A button to reload the data.
 321   
      */
 322  0
     private JButton createReloadButton() {
 323  0
         JButton button = new JButton("Reload");
 324  0
         button.addActionListener(this);
 325   
 
 326  0
         return button;
 327   
     }
 328   
 
 329   
     /**
 330   
      * Method to create the right pane consisting of the JTable.
 331   
      *
 332   
      * @return A JPanel for the right side of the main frame.
 333   
      */
 334  0
     private JScrollPane createRightPanel() {
 335  0
         Vector colNames = new Vector(2);
 336  0
         colNames.add("Prop name");
 337  0
         colNames.add("Value");
 338  0
         m_propTableModel = new DefaultTableModel(colNames, 0);
 339   
 
 340  0
         JTable propTable = new JTable(m_propTableModel);
 341  0
         propTable.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
 342   
 
 343  0
         JScrollPane rightPane = new JScrollPane(propTable);
 344  0
         rightPane.setSize(new Dimension(300, 300));
 345  0
         rightPane.setPreferredSize(rightPane.getSize());
 346  0
         rightPane.setColumnHeaderView(propTable.getTableHeader());
 347   
 
 348  0
         return rightPane;
 349   
     }
 350   
 
 351   
     /**
 352   
      * Method to create required widgets/components and populate
 353   
      * the content pane with them.
 354   
      *
 355   
      * @param pane   The content pane to which the created
 356   
      *               objects have to be added into.
 357   
      */
 358  0
     private void equip(final Container pane) {
 359  0
         pane.setSize(400, 300);
 360  0
         pane.add(
 361   
             new JSplitPane(
 362   
                 JSplitPane.HORIZONTAL_SPLIT,
 363   
                 createLeftPanel(),
 364   
                 createRightPanel()),
 365   
             BorderLayout.CENTER);
 366   
     }
 367   
 }
 368