Frames | No Frames |
1: /* UndoableEditSupport.java -- 2: Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: 39: package javax.swing.undo; 40: 41: import java.util.Iterator; 42: import java.util.Vector; 43: 44: import javax.swing.event.UndoableEditEvent; 45: import javax.swing.event.UndoableEditListener; 46: 47: /** 48: * A helper class for supporting {@link 49: * javax.swing.event.UndoableEditListener}. 50: * 51: * @author Andrew Selkirk (aselkirk@sympatico.ca) 52: * @author Sascha Brawer (brawer@dandelis.ch) 53: */ 54: public class UndoableEditSupport 55: { 56: /** 57: * The number of times that {@link #beginUpdate()} has been called 58: * without a matching call to {@link #endUpdate()}. 59: */ 60: protected int updateLevel; 61: 62: 63: /** 64: * compoundEdit 65: */ 66: protected CompoundEdit compoundEdit; 67: 68: 69: /** 70: * The currently registered listeners. 71: */ 72: protected Vector<UndoableEditListener> listeners = 73: new Vector<UndoableEditListener>(); 74: 75: 76: /** 77: * The source of the broadcast UndoableEditEvents. 78: */ 79: protected Object realSource; 80: 81: 82: /** 83: * Constructs a new helper for broadcasting UndoableEditEvents. The 84: * events will indicate the newly constructed 85: * <code>UndoableEditSupport</code> instance as their source. 86: * 87: * @see #UndoableEditSupport(java.lang.Object) 88: */ 89: public UndoableEditSupport() 90: { 91: realSource = this; 92: } 93: 94: 95: /** 96: * Constructs a new helper for broadcasting UndoableEditEvents. 97: * 98: * @param realSource the source of the UndoableEditEvents that will 99: * be broadcast by this helper. If <code>realSource</code> is 100: * <code>null</code>, the events will indicate the newly constructed 101: * <code>UndoableEditSupport</code> instance as their source. 102: */ 103: public UndoableEditSupport(Object realSource) 104: { 105: if (realSource == null) 106: realSource = this; 107: this.realSource = realSource; 108: } 109: 110: 111: /** 112: * Returns a string representation of this object that may be useful 113: * for debugging. 114: */ 115: public String toString() 116: { 117: // Note that often, this.realSource == this. Therefore, dumping 118: // realSource without additional checks may lead to infinite 119: // recursion. See Classpath bug #7119. 120: return super.toString() + " updateLevel: " + updateLevel 121: + " listeners: " + listeners + " compoundEdit: " + compoundEdit; 122: } 123: 124: 125: /** 126: * Registers a listener. 127: * 128: * @param val the listener to be added. 129: */ 130: public synchronized void addUndoableEditListener(UndoableEditListener val) 131: { 132: listeners.add(val); 133: } 134: 135: 136: /** 137: * Unregisters a listener. 138: * @param val the listener to be removed. 139: */ 140: public synchronized void removeUndoableEditListener(UndoableEditListener val) 141: { 142: listeners.removeElement(val); 143: } 144: 145: 146: /** 147: * Returns an array containing the currently registered listeners. 148: */ 149: public synchronized UndoableEditListener[] getUndoableEditListeners() 150: { 151: UndoableEditListener[] result = new UndoableEditListener[listeners.size()]; 152: return listeners.toArray(result); 153: } 154: 155: 156: /** 157: * Notifies all registered listeners that an {@link 158: * UndoableEditEvent} has occured. 159: * 160: * <p><b>Lack of Thread Safety:</b> It is <em>not</em> safe to call 161: * this method from concurrent threads, unless the call is protected 162: * by a synchronization on this <code>UndoableEditSupport</code> 163: * instance. 164: * 165: * @param edit the edit action to be posted. 166: */ 167: protected void _postEdit(UndoableEdit edit) 168: { 169: UndoableEditEvent event; 170: Iterator<UndoableEditListener> iter; 171: 172: // Do nothing if we have no listeners. 173: if (listeners.isEmpty()) 174: return; 175: 176: event = new UndoableEditEvent(realSource, edit); 177: 178: // We clone the vector because this allows listeners to register 179: // or unregister listeners in their undoableEditHappened method. 180: // Otherwise, this would throw exceptions (in the case of 181: // Iterator, a java.util.ConcurrentModificationException; in the 182: // case of a direct loop over the Vector elements, some 183: // index-out-of-bounds exception). 184: iter = new Vector<UndoableEditListener>(listeners).iterator(); 185: while (iter.hasNext()) 186: iter.next().undoableEditHappened(event); 187: } 188: 189: 190: /** 191: * If {@link #beginUpdate} has been called (so that the current 192: * update level is greater than zero), adds the specified edit 193: * to {@link #compoundEdit}. Otherwise, notify listeners of the 194: * edit by calling {@link #_postEdit(UndoableEdit)}. 195: * 196: * <p><b>Thread Safety:</b> It is safe to call this method from any 197: * thread without external synchronization. 198: * 199: * @param edit the edit action to be posted. 200: */ 201: public synchronized void postEdit(UndoableEdit edit) 202: { 203: if (compoundEdit != null) 204: compoundEdit.addEdit(edit); 205: else 206: _postEdit(edit); 207: } 208: 209: 210: /** 211: * Returns the current update level. 212: */ 213: public int getUpdateLevel() 214: { 215: return updateLevel; 216: } 217: 218: 219: /** 220: * Starts a (possibly nested) update session. If the current update 221: * level is zero, {@link #compoundEdit} is set to the result of the 222: * {@link #createCompoundEdit} method. In any case, the update level 223: * is increased by one. 224: * 225: * <p><b>Thread Safety:</b> It is safe to call this method from any 226: * thread without external synchronization. 227: */ 228: public synchronized void beginUpdate() 229: { 230: if (compoundEdit == null) 231: compoundEdit = createCompoundEdit(); 232: ++updateLevel; 233: } 234: 235: 236: /** 237: * Creates a new instance of {@link CompoundEdit}. Called by {@link 238: * #beginUpdate}. If a subclass wants {@link #beginUpdate} to work 239: * on a specific {@link #compoundEdit}, it should override this 240: * method. 241: * 242: * @return a newly created instance of {@link CompoundEdit}. 243: */ 244: protected CompoundEdit createCompoundEdit() 245: { 246: return new CompoundEdit(); 247: } 248: 249: 250: /** 251: * Ends an update session. If the terminated session was the 252: * outermost session, {@link #compoundEdit} will receive an 253: * <code>end</code> message, and {@link #_postEdit} gets called in 254: * order to notify any listeners. Finally, the 255: * <code>compoundEdit</code> is discarded. 256: * 257: * <p><b>Thread Safety:</b> It is safe to call this method from any 258: * thread without external synchronization. 259: */ 260: public synchronized void endUpdate() 261: { 262: if (updateLevel == 0) 263: throw new IllegalStateException(); 264: 265: if (--updateLevel > 0) 266: return; 267: 268: compoundEdit.end(); 269: _postEdit(compoundEdit); 270: compoundEdit = null; 271: } 272: }