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 listeners = new Vector();
  73: 
  74: 
  75:   /**
  76:    * The source of the broadcast UndoableEditEvents.
  77:    */
  78:   protected Object realSource;
  79: 
  80: 
  81:   /**
  82:    * Constructs a new helper for broadcasting UndoableEditEvents.  The
  83:    * events will indicate the newly constructed
  84:    * <code>UndoableEditSupport</code> instance as their source.
  85:    *
  86:    * @see #UndoableEditSupport(java.lang.Object)
  87:    */
  88:   public UndoableEditSupport()
  89:   {
  90:     realSource = this;
  91:   }
  92: 
  93: 
  94:   /**
  95:    * Constructs a new helper for broadcasting UndoableEditEvents.
  96:    *
  97:    * @param realSource the source of the UndoableEditEvents that will
  98:    * be broadcast by this helper. If <code>realSource</code> is
  99:    * <code>null</code>, the events will indicate the newly constructed
 100:    * <code>UndoableEditSupport</code> instance as their source.
 101:    */
 102:   public UndoableEditSupport(Object realSource)
 103:   {
 104:     if (realSource == null)
 105:       realSource = this;
 106:     this.realSource = realSource;
 107:   }
 108: 
 109: 
 110:   /**
 111:    * Returns a string representation of this object that may be useful
 112:    * for debugging.
 113:    */
 114:   public String toString()
 115:   {
 116:     // Note that often, this.realSource == this. Therefore, dumping
 117:     // realSource without additional checks may lead to infinite
 118:     // recursion. See Classpath bug #7119.
 119:     return super.toString() + " updateLevel: " + updateLevel
 120:       + " listeners: " + listeners + " compoundEdit: " + compoundEdit;
 121:   }
 122: 
 123: 
 124:   /**
 125:    * Registers a listener.
 126:    *
 127:    * @param val the listener to be added.
 128:    */
 129:   public synchronized void addUndoableEditListener(UndoableEditListener val)
 130:   {
 131:     listeners.add(val);
 132:   }
 133: 
 134: 
 135:   /**
 136:    * Unregisters a listener.
 137:    * @param val the listener to be removed.
 138:    */
 139:   public synchronized void removeUndoableEditListener(UndoableEditListener val)
 140:   {
 141:     listeners.removeElement(val);
 142:   }
 143: 
 144: 
 145:   /**
 146:    * Returns an array containing the currently registered listeners.
 147:    */
 148:   public synchronized UndoableEditListener[] getUndoableEditListeners()
 149:   {
 150:     UndoableEditListener[] result = new UndoableEditListener[listeners.size()];
 151:     return (UndoableEditListener[]) listeners.toArray(result);
 152:   }
 153: 
 154: 
 155:   /**
 156:    * Notifies all registered listeners that an {@link
 157:    * UndoableEditEvent} has occured.
 158:    *
 159:    * <p><b>Lack of Thread Safety:</b> It is <em>not</em> safe to call
 160:    * this method from concurrent threads, unless the call is protected
 161:    * by a synchronization on this <code>UndoableEditSupport</code>
 162:    * instance.
 163:    *
 164:    * @param edit the edit action to be posted.
 165:    */
 166:   protected void _postEdit(UndoableEdit edit)
 167:   {
 168:     UndoableEditEvent event;
 169:     Iterator iter;
 170: 
 171:     // Do nothing if we have no listeners.
 172:     if (listeners.isEmpty())
 173:       return;
 174: 
 175:     event = new UndoableEditEvent(realSource, edit);
 176: 
 177:     // We clone the vector because this allows listeners to register
 178:     // or unregister listeners in their undoableEditHappened method.
 179:     // Otherwise, this would throw exceptions (in the case of
 180:     // Iterator, a java.util.ConcurrentModificationException; in the
 181:     // case of a direct loop over the Vector elements, some
 182:     // index-out-of-bounds exception).
 183:     iter = ((Vector) listeners.clone()).iterator();
 184:     while (iter.hasNext())
 185:       ((UndoableEditListener) iter.next()).undoableEditHappened(event);
 186:   }
 187: 
 188: 
 189:   /**
 190:    * If {@link #beginUpdate} has been called (so that the current
 191:    * update level is greater than zero), adds the specified edit
 192:    * to {@link #compoundEdit}. Otherwise, notify listeners of the
 193:    * edit by calling {@link #_postEdit(UndoableEdit)}.
 194:    *
 195:    * <p><b>Thread Safety:</b> It is safe to call this method from any
 196:    * thread without external synchronization.
 197:    *
 198:    * @param edit the edit action to be posted.
 199:    */
 200:   public synchronized void postEdit(UndoableEdit edit)
 201:   {
 202:     if (compoundEdit != null)
 203:       compoundEdit.addEdit(edit);
 204:     else
 205:       _postEdit(edit);
 206:   }
 207: 
 208: 
 209:   /**
 210:    * Returns the current update level.
 211:    */
 212:   public int getUpdateLevel()
 213:   {
 214:     return updateLevel;
 215:   }
 216: 
 217: 
 218:   /**
 219:    * Starts a (possibly nested) update session. If the current update
 220:    * level is zero, {@link #compoundEdit} is set to the result of the
 221:    * {@link #createCompoundEdit} method. In any case, the update level
 222:    * is increased by one.
 223:    *
 224:    * <p><b>Thread Safety:</b> It is safe to call this method from any
 225:    * thread without external synchronization.
 226:    */
 227:   public synchronized void beginUpdate()
 228:   {
 229:     if (compoundEdit == null)
 230:       compoundEdit = createCompoundEdit();
 231:     ++updateLevel;
 232:   }
 233: 
 234: 
 235:   /**
 236:    * Creates a new instance of {@link CompoundEdit}. Called by {@link
 237:    * #beginUpdate}. If a subclass wants {@link #beginUpdate} to work
 238:    * on a specific {@link #compoundEdit}, it should override this
 239:    * method.
 240:    *
 241:    * @return a newly created instance of {@link CompoundEdit}.
 242:    */
 243:   protected CompoundEdit createCompoundEdit()
 244:   {
 245:     return new CompoundEdit();
 246:   }
 247: 
 248: 
 249:   /**
 250:    * Ends an update session. If the terminated session was the
 251:    * outermost session, {@link #compoundEdit} will receive an
 252:    * <code>end</code> message, and {@link #_postEdit} gets called in
 253:    * order to notify any listeners. Finally, the
 254:    * <code>compoundEdit</code> is discarded.
 255:    *
 256:    * <p><b>Thread Safety:</b> It is safe to call this method from any
 257:    * thread without external synchronization.
 258:    */
 259:   public synchronized void endUpdate()
 260:   {
 261:     if (updateLevel == 0)
 262:       throw new IllegalStateException();
 263: 
 264:     if (--updateLevel > 0)
 265:       return;
 266: 
 267:     compoundEdit.end();
 268:     _postEdit(compoundEdit);
 269:     compoundEdit = null;
 270:   }
 271: }