Clover coverage report - DrJava Test Coverage (drjava-20120304-r5456)
Coverage timestamp: Sun Mar 4 2012 03:13:23 CST
file stats: LOC: 955   Methods: 66
NCLOC: 582   Classes: 7
 
 Source file Conditionals Statements Methods TOTAL
ErrorPanel.java 38.3% 64.3% 48.5% 56.5%
coverage coverage
 1    /*BEGIN_COPYRIGHT_BLOCK
 2    *
 3    * Copyright (c) 2001-2010, JavaPLT group at Rice University (drjava@rice.edu)
 4    * All rights reserved.
 5    *
 6    * Redistribution and use in source and binary forms, with or without
 7    * modification, are permitted provided that the following conditions are met:
 8    * * Redistributions of source code must retain the above copyright
 9    * notice, this list of conditions and the following disclaimer.
 10    * * Redistributions in binary form must reproduce the above copyright
 11    * notice, this list of conditions and the following disclaimer in the
 12    * documentation and/or other materials provided with the distribution.
 13    * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
 14    * names of its contributors may be used to endorse or promote products
 15    * derived from this software without specific prior written permission.
 16    *
 17    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20    * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 21    * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 22    * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 23    * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 24    * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 25    * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 26    * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 27    * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28    *
 29    * This software is Open Source Initiative approved Open Source Software.
 30    * Open Source Initative Approved is a trademark of the Open Source Initiative.
 31    *
 32    * This file is part of DrJava. Download the current version of this project
 33    * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
 34    *
 35    * END_COPYRIGHT_BLOCK*/
 36   
 37    package edu.rice.cs.drjava.ui;
 38   
 39    import edu.rice.cs.drjava.DrJava;
 40    import edu.rice.cs.drjava.config.OptionConstants;
 41    import edu.rice.cs.drjava.config.OptionEvent;
 42    import edu.rice.cs.drjava.config.OptionListener;
 43    import edu.rice.cs.drjava.model.OpenDefinitionsDocument;
 44    import edu.rice.cs.drjava.model.SingleDisplayModel;
 45    import edu.rice.cs.drjava.model.DJError;
 46    import edu.rice.cs.drjava.model.compiler.CompilerErrorModel;
 47    import edu.rice.cs.drjava.model.ClipboardHistoryModel;
 48    import edu.rice.cs.util.UnexpectedException;
 49    import edu.rice.cs.util.swing.HighlightManager;
 50    import edu.rice.cs.util.swing.BorderlessScrollPane;
 51    import edu.rice.cs.util.text.SwingDocument;
 52    import edu.rice.cs.drjava.model.print.DrJavaBook;
 53   
 54    import edu.rice.cs.util.swing.RightClickMouseAdapter;
 55   
 56    import java.util.HashMap;
 57    import java.util.Vector;
 58   
 59    import javax.swing.*;
 60    import javax.swing.text.*;
 61    import javax.swing.text.SimpleAttributeSet;
 62    import javax.swing.border.EmptyBorder;
 63    import java.awt.datatransfer.*;
 64    import java.awt.*;
 65    import java.awt.event.MouseAdapter;
 66    import java.awt.event.MouseEvent;
 67    import java.awt.event.ItemListener;
 68    import java.awt.event.ItemEvent;
 69    import java.awt.event.ActionListener;
 70    import java.awt.event.ActionEvent;
 71    import java.awt.print.*;
 72    import java.io.IOException;
 73   
 74    /** This class contains common code and interfaces from CompilerErrorPanel, JUnitPanel, and JavadocErrorPanel.
 75    * TODO: parameterize the types of CompilerErrors (which should be called DJErrors) used here
 76    * @version $Id: ErrorPanel.java 5439 2011-08-11 17:13:04Z rcartwright $
 77    */
 78    public abstract class ErrorPanel extends TabbedPanel implements OptionConstants {
 79   
 80    protected static final SimpleAttributeSet NORMAL_ATTRIBUTES = _getNormalAttributes();
 81    protected static final SimpleAttributeSet BOLD_ATTRIBUTES = _getBoldAttributes();
 82   
 83    /** The total number of errors in the list */
 84    protected volatile int _numErrors;
 85    protected volatile JCheckBox _showHighlightsCheckBox;
 86   
 87    protected volatile SingleDisplayModel _model;
 88   
 89    private volatile JScrollPane _scroller;
 90   
 91    /** This contains the _scroller and the _errorNavPanel. */
 92    private volatile JPanel _leftPanel;
 93   
 94    /** This contains the label, showHighlightsCheckBox, and the customPanel. */
 95    private volatile JPanel _rightPanel;
 96   
 97    private volatile JPanel _errorNavPanel;
 98   
 99    private volatile JPanel _errorNavButtonsPanel;
 100   
 101    /** This JPanel contains each child panel's specific UI components. **/
 102    protected volatile JPanel customPanel;
 103   
 104    private volatile JButton _nextErrorButton;
 105    private volatile JButton _prevErrorButton;
 106   
 107    /** _popupMenu and _popupMenuListener are either both null or both non-null. */
 108    protected volatile JPopupMenu _popupMenu = null;
 109    protected volatile RightClickMouseAdapter _popupMenuListener = null;
 110   
 111    /** Highlight painter for selected list items. */
 112    static volatile ReverseHighlighter.DrJavaHighlightPainter _listHighlightPainter =
 113    new ReverseHighlighter.DrJavaHighlightPainter(DrJava.getConfig().getSetting(COMPILER_ERROR_COLOR));
 114   
 115  6 protected static final SimpleAttributeSet _getBoldAttributes() {
 116  6 SimpleAttributeSet s = new SimpleAttributeSet();
 117  6 StyleConstants.setBold(s, true);
 118  6 return s;
 119    }
 120   
 121  6 protected static final SimpleAttributeSet _getNormalAttributes() {
 122  6 SimpleAttributeSet s = new SimpleAttributeSet();
 123  6 return s;
 124    }
 125   
 126  114 public ErrorPanel(SingleDisplayModel model, MainFrame frame, String tabString, String labelString) {
 127  114 super(frame, tabString);
 128  114 _model = model;
 129   
 130  114 _mainPanel.setLayout(new BorderLayout());
 131   
 132  114 _leftPanel = new JPanel(new BorderLayout());
 133   
 134  114 _errorNavPanel = new JPanel(new GridBagLayout());
 135   
 136   
 137    /******** Initialize the error navigation buttons ********/
 138  114 _errorNavButtonsPanel = new JPanel(new BorderLayout());
 139   
 140  114 _nextErrorButton = new JButton(MainFrame.getIcon("Down16.gif"));//new JButton("Next Error");
 141  114 _prevErrorButton = new JButton(MainFrame.getIcon("Up16.gif"));//new JButton("Prev Error");
 142   
 143  114 _nextErrorButton.setMargin(new Insets(0, 0, 0, 0));
 144  114 _nextErrorButton.setToolTipText("Go to the next error");
 145  114 _prevErrorButton.setMargin(new Insets(0, 0, 0, 0));
 146  114 _prevErrorButton.setToolTipText("Go to the previous error");
 147   
 148   
 149    // _errorPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 3));
 150    // _errorPanel.setPreferredSize(new Dimension(27,35));
 151    // _errorPanel.add(_prevErrorButton);
 152    // _errorPanel.add(_nextErrorButton);
 153    // _uiBox.add(_errorPanel, BorderLayout.WEST);
 154  114 _errorNavButtonsPanel.add(_prevErrorButton, BorderLayout.NORTH);
 155  114 _errorNavButtonsPanel.add(_nextErrorButton, BorderLayout.SOUTH);
 156  114 _errorNavButtonsPanel.setBorder(new EmptyBorder(18, 5, 18, 5)); // 5 pix padding on sides
 157   
 158    // JPanel middlePanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 0, 0));
 159    // middlePanel.add(_errorNavButtonsPanel);
 160   
 161  114 _errorNavPanel.add(_errorNavButtonsPanel);//, BorderLayout.CENTER);
 162  114 _showHighlightsCheckBox = new JCheckBox( "Highlight source", true);
 163   
 164    // _mainPanel.setMinimumSize(new Dimension(225,60));
 165    // We make the vertical scrollbar always there.
 166    // If we don't, when it pops up it cuts away the right edge of the
 167    // text. Very bad.
 168  114 _scroller = new BorderlessScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
 169    JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 170   
 171  114 _leftPanel.add(_scroller, BorderLayout.CENTER);
 172  114 _leftPanel.add(_errorNavPanel, BorderLayout.EAST);
 173   
 174  114 customPanel = new JPanel(new BorderLayout());
 175  114 _rightPanel = new JPanel(new BorderLayout());
 176  114 _rightPanel.setBorder(new EmptyBorder(0,5,0,5)); // 5 pix padding on sides
 177    // uiBox.setBorder(new EmptyBorder(5,0,0,0)); // 5 pix padding on top
 178  114 _rightPanel.add(new JLabel(labelString, SwingConstants.LEFT), BorderLayout.NORTH);
 179  114 _rightPanel.add(customPanel, BorderLayout.CENTER);
 180  114 _rightPanel.add(_showHighlightsCheckBox, BorderLayout.SOUTH);
 181   
 182  114 _mainPanel.add(_leftPanel, BorderLayout.CENTER);
 183  114 _mainPanel.add(_rightPanel, BorderLayout.EAST);
 184   
 185    /** Default copy action. Returns focus to the correct pane. */
 186  114 final Action copyAction = new AbstractAction("Copy Contents to Clipboard", MainFrame.getIcon("Copy16.gif")) {
 187  0 public void actionPerformed(ActionEvent e) {
 188  0 getErrorListPane().selectAll();
 189  0 String t = getErrorListPane().getSelectedText();
 190  0 if (t != null) {
 191  0 if (t.length() != 0) {
 192  0 StringSelection stringSelection = new StringSelection(t);
 193  0 Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
 194  0 clipboard.setContents(stringSelection, getErrorListPane());
 195  0 ClipboardHistoryModel.singleton().put(t);
 196    }
 197    }
 198    }
 199    };
 200  114 addPopupMenu(copyAction);
 201  114 getPopupMenu().add(new AbstractAction("Save Copy of Contents...", MainFrame.getIcon("Save16.gif")) {
 202  0 public void actionPerformed(ActionEvent e) {
 203  0 _frame._saveDocumentCopy(getErrorListPane().getErrorDocument());
 204    }
 205    });
 206  114 getPopupMenu().addSeparator();
 207  114 getPopupMenu().add(new AbstractAction("Print...", MainFrame.getIcon("Print16.gif")) {
 208  0 public void actionPerformed(ActionEvent e) {
 209  0 getErrorListPane().getErrorDocument().print();
 210    }
 211    });
 212  114 getPopupMenu().add(new AbstractAction("Print Preview...", MainFrame.getIcon("PrintPreview16.gif")) {
 213  0 public void actionPerformed(ActionEvent e) {
 214  0 getErrorListPane().getErrorDocument().preparePrintJob();
 215  0 new PreviewErrorFrame();
 216    }
 217    });
 218    }
 219   
 220  114 protected void setErrorListPane(final ErrorListPane elp) {
 221  114 if (_popupMenuListener!=null) {
 222  114 if ((_scroller!=null) &&
 223    (_scroller.getViewport()!=null) &&
 224    (_scroller.getViewport().getView()!=null)) {
 225  0 _scroller.getViewport().getView().removeMouseListener(_popupMenuListener);
 226    }
 227    }
 228   
 229  114 _scroller.setViewportView(elp);
 230   
 231  114 if (_popupMenuListener!=null) {
 232  114 _scroller.getViewport().getView().addMouseListener(_popupMenuListener);
 233    }
 234   
 235  114 _nextErrorButton.setEnabled(false);
 236  114 _nextErrorButton.addActionListener(new ActionListener() {
 237  0 public void actionPerformed(ActionEvent e) {
 238  0 elp.nextError();
 239    // _prevErrorButton.setEnabled(_errorListPane.hasPrevError());
 240    // _nextErrorButton.setEnabled(_errorListPane.hasNextError());
 241    }
 242    });
 243  114 _prevErrorButton.setEnabled(false);
 244  114 _prevErrorButton.addActionListener(new ActionListener() {
 245  0 public void actionPerformed(ActionEvent e) {
 246  0 elp.prevError();
 247    // _prevErrorButton.setEnabled(_errorListPane.hasPrevError());
 248    // _nextErrorButton.setEnabled(_errorListPane.hasNextError());
 249    }
 250    });
 251    }
 252   
 253    /** Changes the font of the error list. */
 254  114 public void setListFont(Font f) {
 255  114 SimpleAttributeSet set = new SimpleAttributeSet();
 256  114 StyleConstants.setFontFamily(set, f.getFamily());
 257  114 StyleConstants.setFontSize(set, f.getSize());
 258  114 StyleConstants.setBold(set, f.isBold());
 259  114 StyleConstants.setItalic(set, f.isItalic());
 260   
 261  114 _updateStyles(set);
 262   
 263  114 getErrorListPane().setFont(f);
 264   
 265  114 ErrorDocument doc = getErrorListPane().getErrorDocument();
 266  114 doc.setCharacterAttributes(0, doc.getLength() + 1, set, false);
 267    }
 268   
 269    /** Updates all document styles with the attributes contained in newSet.
 270    * @param newSet Style containing new attributes to use.
 271    */
 272  114 protected void _updateStyles(AttributeSet newSet) {
 273  114 NORMAL_ATTRIBUTES.addAttributes(newSet);
 274  114 BOLD_ATTRIBUTES.addAttributes(newSet);
 275  114 StyleConstants.setBold(BOLD_ATTRIBUTES, true); // bold should always be bold
 276    }
 277   
 278    abstract protected ErrorListPane getErrorListPane();
 279   
 280  317 protected SingleDisplayModel getModel() { return _model; }
 281   
 282    /** This function returns the correct error model */
 283    abstract protected CompilerErrorModel getErrorModel();
 284   
 285    /** Pane to show compiler errors. Similar to a listbox (clicking selects an item) but items can each wrap, etc. */
 286    public abstract class ErrorListPane extends JEditorPane implements ClipboardOwner {
 287    /** The custom keymap for the error list pane. */
 288    protected volatile Keymap _keymap;
 289   
 290    /** Index into _errorListPositions of the currently selected error. */
 291    private volatile int _selectedIndex;
 292   
 293    /**
 294    * The start position of each error in the list. This position is the place
 295    * where the error starts in the error list, as opposed to the place where
 296    * the error exists in the source.
 297    */
 298    protected volatile Position[] _errorListPositions;
 299   
 300    /** Table mapping Positions in the error list to CompilerErrors. */
 301    protected final HashMap<Position, DJError> _errorTable = new HashMap<Position, DJError>();
 302   
 303    // when we create a highlight we get back a tag we can use to remove it
 304    private volatile HighlightManager.HighlightInfo _listHighlightTag = null;
 305   
 306    private volatile HighlightManager _highlightManager = new HighlightManager(this);
 307   
 308    /** Default cut action. */
 309    volatile Action cutAction = new DefaultEditorKit.CutAction() {
 310  0 public void actionPerformed(ActionEvent e) {
 311  0 if (getSelectedText() != null) {
 312  0 super.actionPerformed(e);
 313  0 String s = edu.rice.cs.util.swing.Utilities.getClipboardSelection(ErrorListPane.this);
 314  0 if ((s != null) && (s.length() != 0)) { ClipboardHistoryModel.singleton().put(s); }
 315    }
 316    }
 317    };
 318   
 319    /** Default copy action. */
 320    volatile Action copyAction = new DefaultEditorKit.CopyAction() {
 321  0 public void actionPerformed(ActionEvent e) {
 322  0 if (getSelectedText() != null) {
 323  0 super.actionPerformed(e);
 324  0 String s = edu.rice.cs.util.swing.Utilities.getClipboardSelection(ErrorListPane.this);
 325  0 if (s != null && s.length() != 0) { ClipboardHistoryModel.singleton().put(s); }
 326    }
 327    }
 328    };
 329   
 330    /** No-op paste action. */
 331    volatile Action pasteAction = new DefaultEditorKit.PasteAction() {
 332  0 public void actionPerformed(ActionEvent e) { }
 333    };
 334   
 335    protected MouseAdapter defaultMouseListener = new MouseAdapter() {
 336  0 public void mousePressed(MouseEvent e) { selectNothing(); }
 337  0 public void mouseReleased(MouseEvent e) {
 338  0 DJError error = _errorAtPoint(e.getPoint());
 339   
 340  0 if (_isEmptySelection() && error != null) getErrorListPane().switchToError(error);
 341  0 else selectNothing();
 342    }
 343    };
 344   
 345    // private Hashtable<Position, DJError> _setUpErrorTable() {
 346    // return new Hashtable<Position, DJError>();
 347    // }
 348   
 349    /** Constructs the CompilerErrorListPane.*/
 350  114 public ErrorListPane() {
 351    // // If we set this pane to be of type text/rtf, it wraps based on words
 352    // // as opposed to based on characters.
 353   
 354  114 setContentType("text/rtf");
 355  114 setDocument(new ErrorDocument(getErrorDocumentTitle()));
 356  114 setHighlighter(new ReverseHighlighter());
 357   
 358  114 addMouseListener(defaultMouseListener);
 359   
 360  114 _selectedIndex = 0;
 361  114 _errorListPositions = new Position[0];
 362   
 363  114 this.setFont(new Font("Courier", 0, 20));
 364   
 365    // We set the editor pane disabled so it won't get keyboard focus,
 366    // which makes it uneditable, and so you can't select text inside it.
 367    //setEnabled(false);
 368   
 369    // Set the editor pane to be uneditable, but allow selecting text.
 370  114 setEditable(false);
 371   
 372  114 DrJava.getConfig().addOptionListener(COMPILER_ERROR_COLOR, new CompilerErrorColorOptionListener());
 373   
 374    // Set the colors.
 375  114 StyleConstants.setForeground(NORMAL_ATTRIBUTES, DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR));
 376  114 StyleConstants.setForeground(BOLD_ATTRIBUTES, DrJava.getConfig().getSetting(DEFINITIONS_NORMAL_COLOR));
 377  114 setBackground(DrJava.getConfig().getSetting(DEFINITIONS_BACKGROUND_COLOR));
 378   
 379    // Add OptionListeners for the colors.
 380  114 DrJava.getConfig().addOptionListener(DEFINITIONS_NORMAL_COLOR, new ForegroundColorListener());
 381  114 DrJava.getConfig().addOptionListener(DEFINITIONS_BACKGROUND_COLOR, new BackgroundColorListener());
 382   
 383    /* Item listener instead of change listener so that this code won't be called (twice) every time the mouse moves
 384    * over the _showHighlightsCheckBox (5/26/05)
 385    */
 386  114 _showHighlightsCheckBox.addItemListener(new ItemListener() {
 387  0 public void itemStateChanged(ItemEvent e) {
 388  0 DefinitionsPane lastDefPane = _frame.getCurrentDefPane();
 389   
 390  0 if (e.getStateChange() == ItemEvent.DESELECTED) {
 391  0 lastDefPane.removeErrorHighlight();
 392    }
 393   
 394  0 else if (e.getStateChange() == ItemEvent.SELECTED) {
 395  0 getErrorListPane().switchToError(getSelectedIndex());
 396    // Commented out because they are redudant; done in switchToError(...)
 397    // DefinitionsPane curDefPane = _frame.getCurrentDefPane();
 398    // lastDefPane.requestFocusInWindow();
 399    // lastDefPane.getCaret().setVisible(true);
 400    }
 401    }
 402    });
 403   
 404  114 _keymap = addKeymap("ERRORLIST_KEYMAP", getKeymap());
 405   
 406  114 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_CUT), cutAction);
 407  114 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_COPY), copyAction);
 408  114 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PASTE_FROM_HISTORY), pasteAction);
 409  114 DrJava.getConfig().addOptionListener(OptionConstants.KEY_CUT, new OptionListener<Vector<KeyStroke>>() {
 410  0 public void optionChanged(OptionEvent<Vector<KeyStroke>> oe) {
 411  0 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_CUT), cutAction);
 412    }
 413    });
 414  114 DrJava.getConfig().addOptionListener(OptionConstants.KEY_COPY, new OptionListener<Vector<KeyStroke>>() {
 415  0 public void optionChanged(OptionEvent<Vector<KeyStroke>> oe) {
 416  0 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_COPY), copyAction);
 417    }
 418    });
 419  114 DrJava.getConfig().addOptionListener(OptionConstants.KEY_PASTE_FROM_HISTORY, new OptionListener<Vector<KeyStroke>>() {
 420  0 public void optionChanged(OptionEvent<Vector<KeyStroke>> oe) {
 421  0 addActionForKeyStroke(DrJava.getConfig().getSetting(OptionConstants.KEY_PASTE_FROM_HISTORY), pasteAction);
 422    }
 423    });
 424    }
 425   
 426    /** Gets the ErrorDocument associated with this ErrorListPane. The inherited getDocument method must be preserved
 427    * because the ErrorListPane constructor uses it fetch a Document that is NOT an ErrorDocument. ErrorListPane
 428    * immediately sets the Document corresponding to this JEditorPane to an ErrorDocument and strictly maintains it as
 429    * an ErrorDocument, but the JEditorPane constructor binds its document to a PlainDocument and uses getDocument
 430    * before ErrorListPane can set this field to an ErrorDocument.
 431    */
 432  156 public ErrorDocument getErrorDocument() { return (ErrorDocument) getDocument(); }
 433   
 434    /** Assigns the given keystroke to the given action in this pane.
 435    * @param stroke keystroke that triggers the action
 436    * @param action Action to perform
 437    */
 438  342 public void addActionForKeyStroke(Vector<KeyStroke> stroke, Action action) {
 439    // remove previous bindings
 440  342 KeyStroke[] keys = _keymap.getKeyStrokesForAction(action);
 441  342 if (keys != null) {
 442  0 for (int i = 0; i < keys.length; i++) {
 443  0 _keymap.removeKeyStrokeBinding(keys[i]);
 444    }
 445    }
 446  342 for (KeyStroke ks: stroke) {
 447  342 _keymap.addActionForKeyStroke(ks, action);
 448    }
 449  342 setKeymap(_keymap);
 450    }
 451   
 452    /** We lost ownership of what we put in the clipboard. */
 453  0 public void lostOwnership(Clipboard clipboard, Transferable contents) {
 454    // ignore
 455    }
 456   
 457   
 458    /** Returns true if the errors should be highlighted in the source
 459    * @return the status of the JCheckBox _showHighlightsCheckBox
 460    */
 461  4 public boolean shouldShowHighlightsInSource() { return _showHighlightsCheckBox.isSelected(); }
 462   
 463    /** Get the index of the current error in the error array. */
 464  84 public int getSelectedIndex() { return _selectedIndex; }
 465   
 466    /** Returns DJError associated with the given visual coordinates. Returns null if none. */
 467  0 protected DJError _errorAtPoint(Point p) {
 468  0 int modelPos = viewToModel(p);
 469   
 470  0 if (modelPos == -1) return null;
 471   
 472    // Find the first error whose position preceeds this model position
 473  0 int errorNum = -1;
 474  0 for (int i = 0; i < _errorListPositions.length; i++) {
 475  0 if (_errorListPositions[i].getOffset() <= modelPos) errorNum = i;
 476  0 else break; // we've gone past the correct error; the last value was right
 477    }
 478   
 479  0 if (errorNum >= 0) return _errorTable.get(_errorListPositions[errorNum]);
 480  0 return null;
 481    }
 482   
 483    /** Returns the index into _errorListPositions corresponding to the given DJError. */
 484  5 private int _getIndexForError(DJError error) {
 485   
 486  0 if (error == null) throw new IllegalArgumentException("Couldn't find index for null error");
 487   
 488  5 for (int i = 0; i < _errorListPositions.length; i++) {
 489  5 DJError e= _errorTable.get(_errorListPositions[i]);
 490  5 if (error.equals(e)) return i;
 491    }
 492   
 493  0 throw new IllegalArgumentException("Couldn't find index for error " + error);
 494    }
 495   
 496    /** Returns true if the text selection interval is empty. */
 497  0 protected boolean _isEmptySelection() { return getSelectionStart() == getSelectionEnd(); }
 498   
 499    /** Update the pane which holds the list of errors for the viewer. */
 500  127 protected void updateListPane(boolean done) {
 501  127 try {
 502  127 _errorListPositions = new Position[_numErrors];
 503  127 _errorTable.clear();
 504   
 505  126 if (_numErrors == 0) _updateNoErrors(done);
 506  1 else _updateWithErrors();
 507    }
 508  0 catch (BadLocationException e) { throw new UnexpectedException(e); }
 509   
 510    // Force UI to redraw
 511  127 repaint();
 512    }
 513   
 514    abstract protected void _updateNoErrors(boolean done) throws BadLocationException;
 515   
 516    abstract protected void _updateWithErrors() throws BadLocationException;
 517   
 518    /** Gets the message indicating the number of errors and warnings.*/
 519  1 protected String _getNumErrorsMessage(String failureName, String failureMeaning) {
 520  1 StringBuilder numErrMsg;
 521   
 522    /** Used for display purposes only */
 523  1 int numCompErrs = getErrorModel().getNumCompErrors();
 524  1 int numWarnings = getErrorModel().getNumWarnings();
 525   
 526  1 if (!getErrorModel().hasOnlyWarnings()) {
 527    //failureName = error or test (for compilation and JUnit testing respectively)
 528  1 numErrMsg = new StringBuilder(numCompErrs + " " + failureName);
 529  0 if (numCompErrs > 1) numErrMsg.append("s");
 530  0 if (numWarnings > 0) numErrMsg.append(" and " + numWarnings + " warning");
 531    }
 532   
 533  0 else numErrMsg = new StringBuilder(numWarnings + " warning");
 534   
 535  0 if (numWarnings > 1) numErrMsg.append("s");
 536   
 537  1 numErrMsg.append(" " + failureMeaning + ":\n");
 538  1 return numErrMsg.toString();
 539    }
 540   
 541    /** Gets the message to title the block containing only errors. */
 542  1 protected String _getErrorTitle() {
 543  1 CompilerErrorModel cem = getErrorModel();
 544  0 if (cem.getNumCompErrors() > 1) return "--------------\n*** Errors ***\n--------------\n";
 545  1 if (cem.getNumCompErrors() > 0) return "-------------\n*** Error ***\n-------------\n";
 546  0 return "";
 547    }
 548   
 549    /** Gets the message to title the block containing only warnings. */
 550  1 protected String _getWarningTitle() {
 551  1 CompilerErrorModel cem = getErrorModel();
 552  0 if (cem.getNumWarnings() > 1) return "--------------\n** Warnings **\n--------------\n";
 553  0 if (cem.getNumWarnings() > 0) return "-------------\n** Warning **\n-------------\n";
 554  1 return "";
 555    }
 556   
 557    /** Used to show that the last compile was unsuccessful.*/
 558  1 protected void _updateWithErrors(String failureName, String failureMeaning, ErrorDocument doc)
 559    throws BadLocationException {
 560    // Print how many errors
 561  1 String numErrsMsg = _getNumErrorsMessage(failureName, failureMeaning);
 562  1 doc.append(numErrsMsg, BOLD_ATTRIBUTES);
 563   
 564  1 _insertErrors(doc);
 565  1 setDocument(doc);
 566   
 567    // Select the first error if there are some errors (i.e. does not select if there are only warnings)
 568  1 if (!getErrorModel().hasOnlyWarnings())
 569  1 getErrorListPane().switchToError(0);
 570    }
 571   
 572    /** Returns true if there is an error after the selected error. */
 573  42 public boolean hasNextError() { return this.getSelectedIndex() + 1 < _numErrors; }
 574   
 575    /** Returns true if there is an error before the selected error. */
 576  42 public boolean hasPrevError() { return this.getSelectedIndex() > 0; }
 577   
 578    /** Switches to the next error. */
 579  0 public void nextError() {
 580    // Select the error
 581  0 if (hasNextError()) {
 582  0 this._selectedIndex += 1;
 583    // Utilities.showDebug("selected index in nextError is " + _selectedIndex + " _numErrors is " + _numErrors);
 584  0 getErrorListPane().switchToError(this.getSelectedIndex());
 585    }
 586    }
 587   
 588    /** Switches to the previous error. */
 589  0 public void prevError() {
 590    // Select the error
 591  0 if (hasPrevError()) {
 592  0 this._selectedIndex -= 1;
 593  0 getErrorListPane().switchToError(this.getSelectedIndex());
 594    }
 595    }
 596   
 597    /** Inserts all of the errors into the given document.
 598    * @param doc the document into which to insert the errors
 599    */
 600  1 protected void _insertErrors(ErrorDocument doc) throws BadLocationException {
 601  1 CompilerErrorModel cem = getErrorModel();
 602  1 int numErrors = cem.getNumErrors();
 603   
 604    //Added this counter in order to add errors and warnings in correct order and select them correctly
 605    //Previous version used errorNum as a counter, but this doesn't work anymore because we are not doing
 606    //errors and variables at the same time.
 607  1 int errorPositionInListOfErrors = 0;
 608    // Show errors first and warnings second
 609   
 610  1 String errorTitle = _getErrorTitle();
 611  0 if (cem.getNumWarnings() > 0) doc.append(errorTitle, BOLD_ATTRIBUTES);
 612   
 613  1 for (int errorNum = 0; errorNum < numErrors; errorNum++) {
 614  1 int startPos = doc.getLength();
 615  1 DJError err = cem.getError(errorNum);
 616   
 617  1 if (!err.isWarning()) {
 618  1 _insertErrorText(err, doc);
 619  1 Position pos = doc.createPosition(startPos);
 620  1 _errorListPositions[errorPositionInListOfErrors] = pos;
 621  1 _errorTable.put(pos, err);
 622  1 errorPositionInListOfErrors++;
 623    }
 624    }
 625   
 626  1 String warningTitle = _getWarningTitle();
 627  1 if (cem.getNumCompErrors() > 0) doc.append(warningTitle, BOLD_ATTRIBUTES);
 628   
 629  1 for (int errorNum = 0; errorNum < numErrors; errorNum++) {
 630  1 int startPos = doc.getLength();
 631  1 DJError err = cem.getError(errorNum);
 632   
 633  1 if (err.isWarning()) {
 634  0 _insertErrorText(err, doc);
 635  0 Position pos = doc.createPosition(startPos);
 636  0 _errorListPositions[errorPositionInListOfErrors] = pos;
 637  0 _errorTable.put(pos, err);
 638  0 errorPositionInListOfErrors++;
 639    }
 640    }
 641    }
 642   
 643    /** Prints a message for the given error
 644    * @param error the error to print
 645    * @param doc the document in the error pane
 646    */
 647  1 protected void _insertErrorText(DJError error, ErrorDocument doc) throws BadLocationException {
 648    // Show file and line number
 649  1 doc.append("File: ", BOLD_ATTRIBUTES);
 650  1 String fileAndLineNumber = error.getFileMessage() + " [line: " + error.getLineMessage() + "]";
 651  1 doc.append(fileAndLineNumber + "\n", NORMAL_ATTRIBUTES);
 652   
 653  0 if (error.isWarning()) doc.append(_getWarningText(), BOLD_ATTRIBUTES);
 654  1 else doc.append(_getErrorText(), BOLD_ATTRIBUTES);
 655   
 656  1 doc.append(error.message(), NORMAL_ATTRIBUTES);
 657  1 doc.append("\n", NORMAL_ATTRIBUTES);
 658    }
 659   
 660    /** Returns the string to identify a warning. */
 661  0 protected String _getWarningText() { return "Warning: "; }
 662   
 663    /** Returns the string to identify an error. */
 664  1 protected String _getErrorText() { return "Error: "; }
 665   
 666    /** When the selection of the current error changes, remove the highlight in the error pane. */
 667  139 protected void _removeListHighlight() {
 668  139 if (_listHighlightTag != null) {
 669  5 _listHighlightTag.remove();
 670  5 _listHighlightTag = null;
 671    }
 672    // _prevErrorButton.setEnabled(false);
 673    // _nextErrorButton.setEnabled(false);
 674    }
 675   
 676    /** Don't select any errors in the error pane. */
 677  129 public void selectNothing() {
 678    // _selectedIndex = -1;
 679  129 _removeListHighlight();
 680   
 681    // Remove highlight from the defPane that has it
 682  129 _frame.getCurrentDefPane().removeErrorHighlight();
 683    }
 684   
 685    /** Selects the given error inside the error list pane. */
 686  5 public void selectItem(DJError error) {
 687    // Utilities.showDebug("selectItem(" + error + ") called");
 688  5 try {
 689    // Find corresponding index
 690  5 int i = _getIndexForError(error);
 691   
 692  5 _selectedIndex = i;
 693    // Utilities.showDebug("selected index = " + i);
 694  5 _removeListHighlight();
 695   
 696  5 int startPos = _errorListPositions[i].getOffset();
 697    // Utilities.showDebug("startPos = " + startPos);
 698   
 699    // end pos is either the end of the document (if this is the last error)
 700    // or the end of the error if the last error (i.e. before the warnings title)
 701    // or the char where the next error starts
 702  5 int endPos;
 703  5 if (i + 1 >= (_numErrors)) endPos = getDocument().getLength();
 704    else {
 705  0 endPos = _errorListPositions[i + 1].getOffset();
 706    // Utilities.showDebug("endPos(before) = " + endPos);
 707  0 DJError nextError = _errorTable.get(_errorListPositions[i+1]);
 708    // Utilities.showDebug("nextError = " + nextError);
 709  0 if (!error.isWarning() && nextError.isWarning()) endPos = endPos - _getWarningTitle().length();
 710    // Utilities.showDebug("endPos(after) = " + endPos);
 711    }
 712   
 713    // Utilities.showDebug("startpos = " + startPos + " endpos = " + endPos);
 714   
 715  5 try {
 716  5 _listHighlightTag = _highlightManager.addHighlight(startPos, endPos, _listHighlightPainter);
 717   
 718    // If first error, show number of errors and warnings preferentially to showing the error
 719    // Otherwise, scroll to make sure this item is visible
 720  5 Rectangle startRect;
 721  5 if (i == 0) startRect = modelToView(0);
 722   
 723  0 else startRect = modelToView(startPos);
 724   
 725  5 Rectangle endRect = modelToView(endPos - 1);
 726   
 727  5 if (startRect != null && endRect != null) {
 728    // Add the end rect onto the start rect to make a rectangle
 729    // that encompasses the entire error
 730  0 startRect.add(endRect);
 731   
 732    //System.err.println("scrll vis: " + startRect);
 733   
 734  0 scrollRectToVisible(startRect);
 735  0 _updateScrollButtons();
 736    }
 737    else {
 738    // Utilities.showDebug("Either startRect or endRect is null!");
 739    // Couldn't draw the box to highlight, so don't highlight anything
 740  5 _removeListHighlight();
 741    }
 742    }
 743    catch (BadLocationException badBadLocation) { }
 744   
 745    }
 746    catch (IllegalArgumentException iae) {
 747    // This shouldn't be happening, but it was reported in bug 704006.
 748    // (_getIndexForError throws it.)
 749    // We'll at least fail a little more gracefully.
 750  0 _removeListHighlight();
 751    }
 752    }
 753   
 754  42 protected void _updateScrollButtons() {
 755  42 if (hasNextError()) {
 756  0 _nextErrorButton.setEnabled(true);
 757    }
 758    else {
 759  42 _nextErrorButton.setEnabled(false);
 760    }
 761  42 if (hasPrevError()) {
 762  0 _prevErrorButton.setEnabled(true);
 763    }
 764    else {
 765  42 _prevErrorButton.setEnabled(false);
 766    }
 767    }
 768   
 769    /** Change all state to select a new error, including moving the caret to the error, if a corresponding position
 770    * exists.
 771    * @param error The error to switch to
 772    */
 773  1 void switchToError(DJError error) {
 774    // Utilities.showDebug("ErrorPanel.switchToError called");
 775  0 if (error == null) return;
 776   
 777  1 final SingleDisplayModel model = getModel();
 778   
 779  1 DefinitionsPane prevPane = _frame.getCurrentDefPane();
 780  1 prevPane.removeErrorHighlight(); // hide previous error highlight
 781  1 OpenDefinitionsDocument prevDoc = prevPane.getOpenDefDocument();
 782   
 783  1 if (error.file() != null) {
 784  1 try {
 785  1 boolean open = false;
 786  1 for(OpenDefinitionsDocument doc : model.getOpenDefinitionsDocuments()) {
 787  2 if((doc.getFile() != null) && (doc.getFile().equals(error.file()))) {
 788  1 open = true;
 789  1 break;
 790    }
 791    }
 792   
 793  1 if(open) {
 794  1 OpenDefinitionsDocument doc = model.getDocumentForFile(error.file());
 795  1 CompilerErrorModel errorModel = getErrorModel();
 796   
 797  1 Position pos = errorModel.getPosition(error); // null if error has no Position
 798    // Utilities.showDebug("The position of the error is: " + pos);
 799    // switch to correct def pane and move caret to error position
 800    // Utilities.showDebug("active document being set to " + doc + " in ErrorPanel.switchToError");
 801   
 802  1 if (! prevDoc.equals(doc)) {
 803  0 model.setActiveDocument(doc);
 804  0 EventQueue.invokeLater(new Runnable() {
 805  0 public void run() {
 806  0 model.addToBrowserHistory();
 807    } });
 808    }
 809  1 else model.refreshActiveDocument();
 810   
 811    // Utilities.showDebug("setting active document has completed");
 812   
 813  1 DefinitionsPane defPane = _frame.getCurrentDefPane();
 814   
 815  1 if (pos != null) {
 816  1 int errPos = pos.getOffset();
 817  1 if (errPos >= 0 && errPos <= doc.getLength()) {
 818  1 defPane.centerViewOnOffset(errPos);
 819   
 820    /* The folowing fixes a bug where, if two consecutive errors are in the same position, the previous error
 821    * is unhighlighted and the new error is not highlighted because the CaretListener does not act because there
 822    * is no change in caret position. (This is the only place where updateHighlight was called from before) */
 823  1 defPane.getErrorCaretListener().updateHighlight(errPos);
 824    }
 825   
 826    }
 827    // The following line is a brute force hack that fixed a bug plaguing the DefinitionsPane immediately after a compilation
 828    // with errors. In some cases (which were consistently reproducible), the DefinitionsPane editing functions would break
 829    // whereby the keystrokes had their usual meaning but incorrect updates were performed in the DefinitionsPane. For example,
 830    // the display behaved as if the editor were in "overwrite" mode.
 831    // _frame._switchDefScrollPane(); // resets an out-of-kilter DefinitionsPane on the first error after a compilation
 832  1 defPane.requestFocusInWindow();
 833  1 defPane.getCaret().setVisible(true);
 834    }
 835    }
 836    catch (IOException ioe) {
 837    // Don't highlight the source if file can't be opened
 838    }
 839    }
 840    // Utilities.showDebug("Calling selectItem(...) from switchToError");
 841    /* setActiveDocument(doc) selects the first error corresponding to the current position (caret location) but this may not
 842    * be the correct error if there are multiple errors for this this position. The following selects the correct error.*/
 843  1 getErrorListPane().selectItem(error);
 844    }
 845   
 846   
 847    /** Another interface to switchToError.
 848    * @param index Index into the array of positions in the CompilerErrorListPane
 849    */
 850  1 void switchToError(int index) {
 851  1 if ((index >= 0) && (index < _errorListPositions.length)) {
 852  1 Position pos = _errorListPositions[index];
 853  1 DJError error= _errorTable.get(pos);
 854  1 switchToError(error);
 855    }
 856    }
 857   
 858    /** The OptionListener for compiler COMPILER_ERROR_COLOR */
 859    private class CompilerErrorColorOptionListener implements OptionListener<Color> {
 860   
 861  0 public void optionChanged(OptionEvent<Color> oce) {
 862  0 _listHighlightPainter = new ReverseHighlighter.DrJavaHighlightPainter(oce.value);
 863  0 if (_listHighlightTag != null) {
 864  0 _listHighlightTag.refresh(_listHighlightPainter);
 865    }
 866    }
 867    }
 868   
 869    /** The OptionListener for compiler DEFINITIONS_NORMAL_COLOR */
 870    private class ForegroundColorListener implements OptionListener<Color> {
 871  0 public void optionChanged(OptionEvent<Color> oce) {
 872  0 StyleConstants.setForeground(NORMAL_ATTRIBUTES, oce.value);
 873  0 StyleConstants.setForeground(BOLD_ATTRIBUTES, oce.value);
 874   
 875    // Re-attribute the existing text with the new color.
 876  0 ErrorDocument doc = getErrorListPane().getErrorDocument();
 877  0 SimpleAttributeSet set = new SimpleAttributeSet();
 878  0 set.addAttribute(StyleConstants.Foreground, oce.value);
 879  0 doc.setCharacterAttributes(0, doc.getLength(), set, false);
 880    }
 881    }
 882   
 883    /** The OptionListener for compiler DEFINITIONS_BACKGROUND_COLOR. */
 884    private class BackgroundColorListener implements OptionListener<Color> {
 885  0 public void optionChanged(OptionEvent<Color> oce) {
 886  0 setBackground(oce.value);
 887  0 ErrorListPane.this.repaint();
 888    }
 889    }
 890  0 public String getErrorDocumentTitle() { return "Errors"; }
 891    }
 892   
 893    public class ErrorDocument extends SwingDocument {
 894    protected volatile DrJavaBook _book;
 895    protected final String _title;
 896  200 public ErrorDocument(String t) { _title = t; }
 897  0 public Pageable getPageable() throws IllegalStateException { return _book; }
 898  0 public void preparePrintJob() {
 899  0 _book = new DrJavaBook(getDocText(0, getLength()), _title, new PageFormat());
 900    }
 901  0 public void print() {
 902  0 preparePrintJob();
 903  0 PrinterJob printJob = PrinterJob.getPrinterJob();
 904  0 printJob.setPageable(_book);
 905  0 try {
 906  0 if (printJob.printDialog()) printJob.print();
 907    }
 908    catch(PrinterException e) {
 909  0 MainFrameStatics.showError(_frame, e, "Print Error", "An error occured while printing.");
 910    }
 911  0 cleanUpPrintJob();
 912    }
 913  0 public void cleanUpPrintJob() { _book = null; }
 914    }
 915   
 916    public class PreviewErrorFrame extends PreviewFrame {
 917  0 public PreviewErrorFrame() throws IllegalStateException {
 918  0 super(ErrorPanel.this._model, ErrorPanel.this._frame, false);
 919    }
 920  0 protected Pageable setUpDocument(SingleDisplayModel model, boolean interactions) {
 921  0 return getErrorListPane().getErrorDocument().getPageable();
 922    }
 923  0 protected void _print() {
 924  0 getErrorListPane().getErrorDocument().print();
 925    }
 926    }
 927   
 928  456 public JPopupMenu getPopupMenu() { return _popupMenu; }
 929   
 930  114 public void addPopupMenu(Action... actions) {
 931  114 if (_popupMenu==null) {
 932  114 _popupMenu = new JPopupMenu();
 933    }
 934    else {
 935  0 _popupMenu.removeAll();
 936  0 removeMouseListener(_popupMenuListener);
 937  0 _scroller.removeMouseListener(_popupMenuListener);
 938  0 _scroller.getViewport().getView().removeMouseListener(_popupMenuListener);
 939    }
 940  114 for(Action a: actions) { _popupMenu.add(a); }
 941  114 _popupMenuListener = new RightClickMouseAdapter() {
 942  0 protected void _popupAction(MouseEvent e) {
 943  0 requestFocusInWindow();
 944  0 _popupMenu.show(e.getComponent(), e.getX(), e.getY());
 945    }
 946    };
 947  114 addMouseListener(_popupMenuListener);
 948  114 if (_scroller!=null) {
 949  114 _scroller.addMouseListener(_popupMenuListener);
 950  114 if (_scroller.getViewport().getView()!=null) {
 951  0 _scroller.getViewport().getView().addMouseListener(_popupMenuListener);
 952    }
 953    }
 954    }
 955    }