Clover coverage report - clover
Coverage timestamp: Sat Oct 8 2005 22:54:17 EDT
file stats: LOC: 404   Methods: 42
NCLOC: 330   Classes: 3
 
 Source file Conditionals Statements Methods TOTAL
StepEditor.java 80% 91.7% 81% 87.5%
coverage coverage
 1   
 package abbot.editor.editors;
 2   
 
 3   
 import java.awt.*;
 4   
 import java.awt.event.*;
 5   
 import java.lang.reflect.Constructor;
 6   
 import java.util.*;
 7   
 
 8   
 import javax.swing.*;
 9   
 import javax.swing.border.EmptyBorder;
 10   
 import javax.swing.event.*;
 11   
 import javax.swing.text.*;
 12   
 
 13   
 import abbot.Log;
 14   
 import abbot.i18n.Strings;
 15   
 import abbot.script.*;
 16   
 import abbot.editor.widgets.ArrayEditor;
 17   
 import abbot.editor.widgets.TextArea;
 18   
 import abbot.editor.widgets.TextField;
 19   
 
 20   
 /** Provide base-level step editor support with step change notification.  */
 21   
 // NOTE: this should really be done with beans instead...
 22   
 
 23   
 public abstract class StepEditor extends JPanel
 24   
     implements ActionListener, Scrollable, XMLConstants {
 25   
 
 26   
     private Step step;
 27   
     private JLabel label;
 28   
     JTextField description;
 29   
     private LayoutManager layout;
 30   
     private ArrayList listeners = new ArrayList();
 31   
 
 32   
     protected static final int MARGIN = 4;
 33   
     private boolean fieldChanging = false;
 34   
 
 35   
     protected static Color DEFAULT_FOREGROUND = null;
 36   
     protected static Color ERROR_FOREGROUND = Color.red;
 37   
 
 38  27
     public StepEditor(Step step) {
 39  27
         setBorder(new EmptyBorder(2,2,2,2));
 40  27
         setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
 41  27
         layout = getLayout();
 42  27
         this.step = step;
 43  27
         label = new JLabel(step.getXMLTag());
 44  27
         label.setFont(label.getFont().deriveFont(Font.BOLD));
 45  27
         label.setToolTipText(step.getUsage());
 46  27
         add(label);
 47  27
         description = addTextField(null, step.getDescription());
 48  27
         description.setName(TAG_DESC);
 49  27
         description.setToolTipText(Strings.get("editor.step.description.tip"));
 50  27
         if (DEFAULT_FOREGROUND == null) {
 51  9
             DEFAULT_FOREGROUND = description.getForeground();
 52   
         }
 53   
     }
 54   
 
 55   
     /** Keep a reasonable minimum width. */
 56  2
     public Dimension getMinimumSize() {
 57  2
         Dimension min = super.getMinimumSize();
 58  2
         min.width = 200;
 59  2
         return min;
 60   
     }
 61   
 
 62   
     /** Keep a reasonable minimum width. */
 63  23
     public Dimension getPreferredSize() {
 64  23
         Dimension size = super.getPreferredSize();
 65  23
         size.width = 200;
 66  23
         return size;
 67   
     }
 68   
 
 69   
     /** We don't want to become infinitely wide due to text fields. */
 70  0
     public Dimension getMaximumSize() {
 71  0
         Dimension max = super.getMaximumSize();
 72  0
         max.width = 400;
 73  0
         return max;
 74   
     }
 75   
 
 76  20
     protected JCheckBox addCheckBox(String title, boolean value) {
 77  20
         JCheckBox cb = new JCheckBox(title);
 78  20
         cb.setSelected(value);
 79  20
         cb.addActionListener(this);
 80  20
         add(cb);
 81  20
         return cb;
 82   
     }
 83   
 
 84   
     /** Provide a combo box that short-circuits unnecessary and
 85   
         problem-causing event notifications.
 86   
     */
 87   
     private class ComboBox extends JComboBox {
 88   
         private JTextField editor;
 89   
         private boolean configuringEditor;
 90  0
         public ComboBox() { }
 91  10
         public ComboBox(ComboBoxModel model) { super(model); }
 92  23
         public ComboBox(Object[] values) { super(values); }
 93  92
         public void addImpl(Component c, Object constraints, int index) {
 94  92
             if (c instanceof JTextField) {
 95  24
                 editor = (JTextField)c;
 96  24
                 TextField.decorate(editor);
 97   
             }
 98  92
             super.addImpl(c, constraints, index);
 99   
         }
 100   
 
 101   
         /** Disallow recursive calls, which occur when someone sets the combo
 102   
             box contents in response to the combo box selection. */
 103  124
         public void configureEditor(ComboBoxEditor editor, Object item) {
 104   
             // Avoids IllegalStateExceptions from the text field ("Attempt to
 105   
             // mutate in notification" errors).
 106  124
             if (!configuringEditor) {
 107  60
                 configuringEditor = true;
 108  60
                 super.configureEditor(editor, item);
 109  60
                 configuringEditor = false;
 110   
             }
 111   
         }
 112   
 
 113   
         /** Sets the foreground color of the editor text. */
 114  131
         public void setForeground(Color c) {
 115  131
             if (editor != null) {
 116  98
                 editor.setForeground(c);
 117   
             }
 118   
         }
 119   
 
 120   
         /** Overridden to prevent recursive calls. */
 121  123
         public void fireActionEvent() {
 122  123
             if (!fieldChanging) {
 123  95
                 fieldChanging = true;
 124  95
                 super.fireActionEvent();
 125  95
                 fieldChanging = false;
 126   
             }
 127   
         }
 128   
         /** Overridden to prevent recursive calls. */
 129  137
         public void fireItemStateChanged(ItemEvent e) {
 130  137
             if (!fieldChanging) {
 131  108
                 fieldChanging = true;
 132  108
                 super.fireItemStateChanged(e);
 133  108
                 fieldChanging = false;
 134   
             }
 135   
         }
 136   
     }
 137   
 
 138   
     private static final String NONE = "<None>";
 139   
     private class RefModel
 140   
         extends AbstractListModel implements ComboBoxModel {
 141   
         private Resolver resolver;
 142   
         private boolean includeNone;
 143   
         private Object selected;
 144   
         private Collection set;
 145  10
         public RefModel(Resolver r, boolean includeNone) {
 146  10
             resolver = r;
 147  10
             this.includeNone = includeNone;
 148  10
             set = resolver.getComponentReferences();
 149   
         }
 150  199
         public Object getElementAt(int i) {
 151  199
             checkContents();
 152  199
             if (includeNone) {
 153  135
                 if (i == 0)
 154  38
                     return NONE;
 155  97
                 --i;
 156   
             }
 157  161
             return ((ComponentReference)set.toArray()[i]).getID();
 158   
         }
 159  433
         public int getSize() { 
 160  433
             checkContents();
 161  433
             int size = set.size();
 162  433
             return includeNone ? size + 1 : size;
 163   
         }
 164  16
         public void setSelectedItem(Object o) {
 165  16
             if (o instanceof ComponentReference)
 166  0
                 o = ((ComponentReference)o).getID();
 167  16
             else if (o == null)
 168  9
                 o = NONE;
 169  16
             selected = o;
 170  16
             checkContents();
 171   
         }
 172  97
         public Object getSelectedItem() {
 173  97
             checkContents();
 174  97
             return selected == NONE ? null : selected;
 175   
         }
 176   
         // Always check whether this model is synched with the resolver's set
 177   
         // of references.
 178  745
         private void checkContents() {
 179  745
             Collection current = resolver.getComponentReferences();
 180  745
             if (set.size() != current.size()) {
 181  3
                 set = current;
 182  3
                 if (!fieldChanging) {
 183  3
                     fieldChanging = true;
 184  3
                     fireContentsChanged(this, 0, set.size()-1);
 185  3
                     fieldChanging = false;
 186   
                 }
 187   
             }
 188   
         }
 189   
     }
 190   
 
 191  10
     protected JComboBox addComponentSelector(String title, String refid,
 192   
                                              Resolver resolver, 
 193   
                                              boolean allowNone) {
 194   
         // NOTE: the combo box has no method of refreshing its contents when
 195   
         // references are added/removed/changed in the resolver
 196  10
         JComboBox cb = new ComboBox(new RefModel(resolver, allowNone));
 197  10
         cb.setSelectedItem(refid);
 198  10
         cb.addActionListener(this);
 199  10
         add(title, cb);
 200  10
         return cb;
 201   
     }
 202   
 
 203  23
     protected JComboBox addComboBox(String title,
 204   
                                     Object value, Object[] values) {
 205  23
         JComboBox cb = new ComboBox(values);
 206  23
         cb.setEditable(true);
 207  23
         cb.setSelectedItem(value);
 208  23
         cb.addActionListener(this);
 209  23
         add(title, cb);
 210  23
         return cb;
 211   
     }
 212   
 
 213  60
     protected JTextField addTextField(String title, String value) {
 214  60
         return addTextField(title, value, null);
 215   
     }
 216   
 
 217  64
     protected JTextField addTextField(String title, String value,
 218   
                                       String defaultValue) {
 219  64
         JTextField field =
 220   
             new abbot.editor.widgets.TextField(value, defaultValue);
 221  64
         field.addActionListener(this);
 222  64
         add(title, field);
 223  64
         return field;
 224   
     }
 225   
 
 226  27
     protected ArrayEditor addArrayEditor(String title, Object[] values) {
 227  27
         ArrayEditor ed = new ArrayEditor(values);
 228  27
         ed.addActionListener(this);
 229   
         // Make sure we resize/repaint when items are added or removed
 230  27
         ed.addActionListener(new ActionListener() {
 231  47
             public void actionPerformed(ActionEvent e) {
 232  47
                 if (e.getActionCommand() != ArrayEditor.ACTION_ITEM_CHANGED) {
 233  7
                     revalidate();
 234  7
                     repaint();
 235   
                 }
 236   
             }
 237   
         });
 238  27
         add(title, ed);
 239  27
         return ed;
 240   
     }
 241   
 
 242  1
     protected JButton addButton(String title) {
 243  1
         JButton button = new JButton(title);
 244  1
         button.addActionListener(this);
 245  1
         add(button);
 246  1
         return button;
 247   
     }
 248   
 
 249  1
     protected JTextArea addTextArea(String title, String value) {
 250  1
         final TextArea text = new TextArea(value != null ? value : "");
 251  1
         text.setLineWrap(true);
 252  1
         text.setWrapStyleWord(true);
 253  1
         text.setBorder(UIManager.getBorder("TextField.border"));
 254  1
         text.addActionListener(this);
 255  1
         add(title, text);
 256  1
         return text;
 257   
     }
 258   
 
 259   
     /** Automatically remove the strut spacing and the component. */
 260  12
     public void remove(Component comp) {
 261  12
         if (getLayout() == layout) {
 262  12
             Component[] children = super.getComponents();
 263  312
             for (int i=1;i < children.length;i++) {
 264  312
                 if (children[i] == comp) {
 265  12
                     super.remove(children[i-1]);
 266  12
                     break;
 267   
                 }
 268   
             }
 269   
         }
 270  12
         super.remove(comp);
 271   
     }
 272   
 
 273   
     /** Auto-add a label with a component. */
 274  125
     public Component add(String name, Component comp) {
 275  125
         if (name != null) {
 276  97
             JLabel label = new JLabel(name);
 277  97
             label.setLabelFor(comp);
 278  97
             add(label);
 279   
         }
 280  125
         return add(comp);
 281   
     }
 282   
 
 283   
     /** Automatically add a vertical struct with a component. */
 284  277
     public Component add(Component comp) {
 285  277
         if (getLayout() == layout) {
 286  277
             super.add(Box.createVerticalStrut(MARGIN));
 287  277
             if (comp instanceof JComponent) {
 288  277
                 ((JComponent)comp).setAlignmentX(JComponent.LEFT_ALIGNMENT);
 289   
             }
 290   
         }
 291  277
         return super.add(comp);
 292   
     }
 293   
 
 294   
     /** Respond to UI changes by updating the step data. */
 295  71
     public void actionPerformed(ActionEvent ev) {
 296  71
         Object src = ev.getSource();
 297  71
         if (src == description) {
 298   
             // When the description is cleared (but only when entered by ENTER
 299   
             // or FOCUS events), reset it to the default
 300  70
             String text = description.getText();
 301  70
             String cmd = ev.getActionCommand();
 302  70
             if ("".equals(text)) {
 303  26
                 if (!TextField.isDocumentAction(ev.getActionCommand())) {
 304  2
                     SwingUtilities.invokeLater(new Runnable() {
 305  2
                         public void run() {
 306  2
                             description.setText(step.getDefaultDescription());
 307  2
                             description.selectAll();
 308   
                         }
 309   
                     });
 310  2
                     step.setDescription(null);
 311  2
                     fireStepChanged();
 312   
                 }
 313   
             }
 314   
             // Only explicitly set the step data if the data is different
 315   
             // from the default.
 316  44
             else if (cmd == TextField.ACTION_TEXT_REVERTED
 317   
                        || !text.equals(step.getDefaultDescription())) {
 318  18
                 step.setDescription(text);
 319  18
                 fireStepChanged();
 320   
             }
 321   
         }
 322   
     }
 323   
 
 324  2
     public void addStepChangeListener(StepChangeListener scl) {
 325  2
         synchronized(listeners) {
 326  2
             listeners.add(scl);
 327   
         }
 328   
     }
 329   
 
 330  0
     public void removeStepChangeListener(StepChangeListener scl) {
 331  0
         synchronized(listeners) {
 332  0
             listeners.remove(scl);
 333   
         }
 334   
     }
 335   
 
 336   
     /** This method should be invoked after any change to step data. */
 337  131
     protected void fireStepChanged() {
 338  131
         synchronized(listeners) {
 339  131
             Iterator iter = listeners.iterator();
 340  131
             while (iter.hasNext()) {
 341  0
                 StepChangeListener scl = (StepChangeListener)iter.next();
 342  0
                 scl.stepChanged(step);
 343   
             }
 344   
         }
 345   
         // The default description may have changed; ensure the text field is
 346   
         // up to date 
 347  131
         if (!description.getText().equals(step.getDescription())) {
 348  24
             description.setText(step.getDescription());
 349   
         }
 350   
     }
 351   
 
 352   
     /** Return the appropriate editor panel for the given Step.
 353   
      * Custom editors must be named after the step class name, and be defined
 354   
      * in the abbot.editor.editors package, e.g. abbot.script.Launch expects
 355   
      * abbot.editor.editors.LaunchEditor, abbot.script.Assert expects
 356   
      * abbot.editor.editors.AssertEditor. 
 357   
      */
 358  4
     public static StepEditor getEditor(Step step) {
 359  4
         Class stepClass = step.getClass();
 360  4
         Log.debug("Looking up editor for " + step + " using " + stepClass);
 361  4
         String className = stepClass.getName();
 362  4
         className = "abbot.editor.editors."
 363   
             + className.substring(className.lastIndexOf(".") + 1) + "Editor";
 364  4
         try {
 365  4
             Log.debug("Trying " + className);
 366  4
             Class cls = Class.forName(className);
 367  2
             Class[] types = new Class[] { stepClass };
 368  2
             Constructor ctor = cls.getConstructor(types);
 369  2
             return (StepEditor)ctor.newInstance(new Object[] { step });
 370   
         }
 371   
         catch(ClassNotFoundException e) {
 372   
             // ignore this one
 373   
         }
 374   
         catch(Exception e) {
 375  0
             Log.warn(e);
 376   
         }
 377  2
         return null;
 378   
     }
 379