Source for javax.swing.undo.UndoableEditSupport

   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 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 = ((Vector) listeners.clone()).iterator();
 185:     while (iter.hasNext())
 186:       ((UndoableEditListener) 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: }