Clover coverage report - DrJava Test Coverage (drjava-20110828-r5448)
Coverage timestamp: Sun Aug 28 2011 03:13:33 CDT
file stats: LOC: 253   Methods: 19
NCLOC: 101   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
CompoundUndoManager.java 77.8% 87.9% 78.9% 84.2%
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.model.definitions;
 38   
 39    import java.awt.EventQueue;
 40    import java.util.LinkedList;
 41    import javax.swing.undo.*;
 42   
 43    import edu.rice.cs.drjava.model.GlobalEventNotifier;
 44   
 45    /** Extended UndoManager with increased functionality. Can handle aggregating multiple edits into one for the purposes
 46    * of undoing and redoing. It exposes editToBeUndone and editToBeRedone (under new names); they are protected methods
 47    * in UndoManager. The public methods that involve composite state are synchronized, so this manager can be accessed
 48    * outside of the event thread. The internal data structures _compoundEdits and _keys are not thread safe but they
 49    * only accessed only by synchronized methods. The synchronization scheme (locking on this) follows UndoManager.
 50    * @version $Id: CompoundUndoManager.java 5361 2010-08-13 22:39:45Z mgricken $
 51    */
 52    public class CompoundUndoManager extends UndoManager {
 53   
 54    static edu.rice.cs.util.Log LOG = new edu.rice.cs.util.Log("CompoundUndoManager.txt", false);
 55   
 56    private static volatile int counter = 0;
 57   
 58    private final int id;
 59   
 60    /** The compound edits we are storing. Not thread safe! */
 61    private final LinkedList<CompoundEdit> _compoundEdits;
 62   
 63    /** The keys for the CompoundEdits we are storing. */
 64    private final LinkedList<Integer> _keys;
 65   
 66    /** The next key to use for nested CompoundEdits. */
 67    private volatile int _nextKey;
 68   
 69    /** The last edit that was performed before the last save. */
 70    private volatile UndoableEdit _savePoint;
 71   
 72    /** Keeps track of the listeners to this undo manager. */
 73    private final GlobalEventNotifier _notifier;
 74   
 75    /** Standard constructor. */
 76  1086 public CompoundUndoManager(GlobalEventNotifier notifier) {
 77  1086 super();
 78  1086 counter++;
 79  1086 id = counter;
 80  1086 _compoundEdits = new LinkedList<CompoundEdit>();
 81  1086 _keys = new LinkedList<Integer>();
 82  1086 _nextKey = 0;
 83  1086 _savePoint = null;
 84  1086 _notifier = notifier;
 85    }
 86   
 87    /** Starts a compound edit.
 88    * @return the key for the compound edit
 89    */
 90  56 public /* synchronized */ int startCompoundEdit() {
 91  56 _compoundEdits.add(0, new CompoundEdit());
 92  56 _keys.add(0, Integer.valueOf(_nextKey));
 93  56 if (_nextKey < Integer.MAX_VALUE) _nextKey++;
 94  0 else _nextKey = Integer.MIN_VALUE;
 95  56 return _keys.get(0).intValue();
 96    }
 97   
 98    /** Ends the last compound edit that was created. Used when a compound edit is created by the _undoListener in
 99    * DefinitionsPane and the key is not known in DefinitionsDocument.
 100    */
 101  77 public /* synchronized */ void endLastCompoundEdit() {
 102  73 if (_keys.size() == 0) return;
 103    // NOTE: The preceding can happen if for example uncomment lines does not modify any text.
 104  4 endCompoundEdit(_keys.get(0).intValue());
 105    }
 106   
 107    /** Ends a compound edit.
 108    * @param key the key that was returned by startCompoundEdit()
 109    */
 110  29 public /* synchronized */ void endCompoundEdit(int key) {
 111  2 if (_keys.size() == 0) return;
 112   
 113  27 if (_keys.get(0) == key) {
 114  27 _keys.remove(0);
 115  27 final CompoundEdit ce = _compoundEdits.remove(0);
 116   
 117  27 ce.end();
 118  27 if (ce.canUndo()) {
 119  27 if (! _compoundEditInProgress()) {
 120  27 super.addEdit(ce);
 121  27 _notifyUndoHappened();
 122    }
 123    else {
 124  0 _compoundEdits.get(0).addEdit(ce);
 125    }
 126    }
 127    }
 128  0 else throw new IllegalStateException("Improperly nested compound edits.");
 129    }
 130   
 131    /** Gets the last Compound Edit entered into the list. Used in making a Compound edit for granular undo. */
 132  0 public /* synchronized */ CompoundEdit getLastCompoundEdit() { return _compoundEdits.get(0); }
 133   
 134    /** Gets the next undo.
 135    * @return the next undo
 136    */
 137  0 public UndoableEdit getNextUndo() { return editToBeUndone(); }
 138   
 139    /** Gets the next redo.
 140    * @return the next redo
 141    */
 142  0 public UndoableEdit getNextRedo() { return editToBeRedone(); }
 143   
 144    /** Adds an edit. Checks whether or not the current edit is a compound edit.
 145    * @param e the edit to be added
 146    * @return true if the add is successful, false otherwise
 147    */
 148  303 public /* synchronized */ boolean addEdit(UndoableEdit e) {
 149  303 if (_compoundEditInProgress()) {
 150    // _notifyUndoHappened(); // added this for granular undo
 151  302 return _compoundEdits.get(0).addEdit(e);
 152    }
 153    else {
 154  1 boolean result = super.addEdit(e);
 155  1 _notifyUndoHappened();
 156  1 return result;
 157    }
 158    }
 159   
 160    /** Returns whether or not a compound edit is in progress.
 161    * @return true iff in progress
 162    */
 163  1683 public /* synchronized */ boolean _compoundEditInProgress() { return ! _compoundEdits.isEmpty(); }
 164   
 165    /** Returns true when a compound edit is in progress, or when there are valid stored undoable edits
 166    * @return true iff undoing is possible
 167    */
 168  420 public /* synchronized */ boolean canUndo() {
 169  420 LOG.log("canUndo: _compoundEditInProgress() = "+_compoundEditInProgress()+", super.canUndo() = "+super.canUndo());
 170  420 LOG.log(" "+_compoundEdits);
 171  420 return _compoundEditInProgress() || super.canUndo(); }
 172   
 173    /** Returns the presentation name for this undo, or delegates to super if none is available
 174    * @return the undo's presentation name
 175    */
 176  68 public /* synchronized */ String getUndoPresentationName() {
 177  59 if (_compoundEditInProgress()) return "Undo Previous Command";
 178  9 return super.getUndoPresentationName();
 179    }
 180   
 181    /** Undoes the last undoable edit, or compound edit created by the user. */
 182  9 public /* synchronized */ void undo() {
 183  9 endCompoundEdit();
 184  9 super.undo();
 185    }
 186   
 187    // Not currently used.
 188    // /** Overload for undo which allows the initiator of a CompoundEdit to abandon it.
 189    // * WARNING: this has been used to date and has not been properly tested and very possibly may not work.
 190    // * @param key the key returned by the last call to startCompoundEdit
 191    // * @throws IllegalArgumentException if the key is incorrect
 192    // */
 193    // public synchronized void undo(int key) {
 194    // if (_keys.get(0) == key) {
 195    // final CompoundEdit ce = _compoundEdits.get(0);
 196    // _compoundEdits.remove(0);
 197    // _keys.remove(0);
 198    //
 199    // EventQueue.invokeLater(new Runnable() {
 200    // public void run() {
 201    // ce.end();
 202    // ce.undo();
 203    // ce.die();
 204    // }
 205    // }); // unsafe methods inherited from CompoundEdit
 206    // }
 207    // else throw new IllegalArgumentException("Bad undo key " + key + "!");
 208    // }
 209   
 210    /** Overrides redo so that any compound edit in progress is ended before the redo is performed. */
 211  4 public /* synchronized */ void redo() {
 212  4 endCompoundEdit();
 213  4 super.redo();
 214    }
 215   
 216    /** Helper method to notify the view that an undoable edit has occured. Note that lock on this is not held by
 217    * the event thread (even if called from event thread) when notification happens.
 218    */
 219  28 private void _notifyUndoHappened() {
 220    // Use SwingUtilities.invokeLater so that notification is deferred when running in the event thread.
 221  28 EventQueue.invokeLater(new Runnable() { public void run() { _notifier.undoableEditHappened(); } });
 222    }
 223   
 224    /** Ends the compoundEdit in progress if any. Used by undo(), redo(), documentSaved(). */
 225  445 private /* synchronized */ void endCompoundEdit() {
 226  445 Integer[] keys = _keys.toArray(new Integer[_keys.size()]); // unit testing ran into a concurrent modification exception without this copying operation
 227  445 if (_compoundEditInProgress()) {
 228  2 for (int key: keys) endCompoundEdit(key);
 229    }
 230    }
 231   
 232    /** Informs this undo manager that the document has been saved. */
 233  432 public /* synchronized */ void documentSaved() {
 234  432 endCompoundEdit();
 235  432 _savePoint = editToBeUndone();
 236    // Utilities.showDebug("_savePoint := " + _savePoint);
 237    }
 238   
 239    /** Determines if the document is in the same undo state as it was when it was last saved.
 240    * @return true iff all changes have been undone since the last save
 241    */
 242  1 public /* synchronized */ boolean isModified() {
 243    // Utilities.showDebug("_savePoint = " + _savePoint + " editToBeUndone() = " + editToBeUndone());
 244  1 return editToBeUndone() != _savePoint;
 245    }
 246   
 247  0 public String toString() { return "(CompoundUndoManager: " + id + ")"; }
 248   
 249    /** Used to help track down memory leaks. */
 250    // protected void finalize() throws Throwable{
 251    // super.finalize();
 252    // }
 253    }