Source for javax.swing.text.StringContent

   1: /* StringContent.java --
   2:    Copyright (C) 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.text;
  40: 
  41: import java.io.Serializable;
  42: import java.util.Iterator;
  43: import java.util.Vector;
  44: 
  45: import javax.swing.undo.AbstractUndoableEdit;
  46: import javax.swing.undo.CannotRedoException;
  47: import javax.swing.undo.CannotUndoException;
  48: import javax.swing.undo.UndoableEdit;
  49: 
  50: /**
  51:  * An implementation of the <code>AbstractDocument.Content</code>
  52:  * interface useful for small documents or debugging. The character
  53:  * content is a simple character array. It's not really efficient.
  54:  * 
  55:  * <p>Do not use this class for large size.</p>
  56:  */
  57: public final class StringContent 
  58:   implements AbstractDocument.Content, Serializable
  59: {
  60:   /** The serialization UID (compatible with JDK1.5). */
  61:   private static final long serialVersionUID = 4755994433709540381L;
  62: 
  63:   // This is package-private to avoid an accessor method.
  64:   char[] content;
  65: 
  66:   private int count;
  67: 
  68:   private Vector positions = new Vector();
  69: 
  70:   private class InsertUndo extends AbstractUndoableEdit
  71:   {
  72:     private int start;
  73:     
  74:     private int length;
  75: 
  76:     private String redoContent;
  77: 
  78:     public InsertUndo(int start, int length)
  79:     {
  80:       super();
  81:       this.start = start;
  82:       this.length = length;
  83:     }
  84: 
  85:     public void undo()
  86:     {
  87:       super.undo();
  88:       try
  89:         {
  90:           StringContent.this.checkLocation(this.start, this.length);
  91:           this.redoContent = new String(StringContent.this.content, this.start,
  92:               this.length);
  93:           StringContent.this.remove(this.start, this.length);
  94:         }
  95:       catch (BadLocationException b)
  96:         {
  97:           throw new CannotUndoException();
  98:         }
  99:     }
 100:     
 101:     public void redo()
 102:     {
 103:       super.redo();
 104:       try
 105:         {
 106:           StringContent.this.insertString(this.start, this.redoContent);
 107:         }
 108:       catch (BadLocationException b)
 109:         {
 110:           throw new CannotRedoException();
 111:         }
 112:     }
 113:   }
 114: 
 115:   private class RemoveUndo extends AbstractUndoableEdit
 116:   {
 117:     private int start;
 118: 
 119:     private String undoString;
 120: 
 121:     public RemoveUndo(int start, String str)
 122:     {
 123:       super();
 124:       this.start = start;
 125:       this.undoString = str;
 126:     }
 127: 
 128:     public void undo()
 129:     {
 130:       super.undo();
 131:       try
 132:         {
 133:           StringContent.this.insertString(this.start, this.undoString);
 134:         }
 135:       catch (BadLocationException bad)
 136:         {
 137:           throw new CannotUndoException();
 138:         }
 139:     }
 140: 
 141:     public void redo()
 142:     {
 143:       super.redo();
 144:       try
 145:         {
 146:           int end = this.undoString.length();
 147:           StringContent.this.remove(this.start, end);
 148:         }
 149:       catch (BadLocationException bad)
 150:         {
 151:           throw new CannotRedoException();
 152:         }
 153:     }
 154:   }
 155: 
 156:   private class StickyPosition implements Position
 157:   {
 158:     private int offset = -1;
 159: 
 160:     public StickyPosition(int offset)
 161:     {
 162:       this.offset = offset;
 163:     }
 164: 
 165:     // This is package-private to avoid an accessor method.
 166:     void setOffset(int offset)
 167:     {
 168:       this.offset = this.offset >= 0 ? offset : -1;
 169:     }
 170: 
 171:     /**
 172:      * Should be >=0.
 173:      */
 174:     public int getOffset()
 175:     {
 176:       return offset < 0 ? 0 : offset;
 177:     }
 178:   }
 179: 
 180:   /**
 181:    * Creates a new instance containing the string "\n".
 182:    */
 183:   public StringContent()
 184:   {
 185:     this(1);
 186:   }
 187: 
 188:   /**
 189:    * Creates a new instance containing the string "\n".
 190:    * 
 191:    * @param initialLength  the initial length of the underlying character 
 192:    *                       array used to store the content.
 193:    */
 194:   public StringContent(int initialLength)
 195:   {
 196:     super();
 197:     if (initialLength < 1)
 198:       initialLength = 1;
 199:     this.content = new char[initialLength];
 200:     this.content[0] = '\n';
 201:     this.count = 1;
 202:   }
 203: 
 204:   protected Vector getPositionsInRange(Vector v,
 205:                                        int offset,
 206:                                        int length)
 207:   {
 208:     Vector refPos = new Vector();
 209:     Iterator iter = this.positions.iterator();
 210:     while(iter.hasNext())
 211:       {
 212:         Position p = (Position) iter.next();
 213:         if ((offset <= p.getOffset())
 214:             && (p.getOffset() <= (offset + length)))
 215:           refPos.add(p);
 216:       }
 217:     return refPos;
 218:   }
 219: 
 220:   /**
 221:    * Creates a position reference for the character at the given offset.  The
 222:    * position offset will be automatically updated when new characters are
 223:    * inserted into or removed from the content.
 224:    * 
 225:    * @param offset  the character offset.
 226:    * 
 227:    * @throws BadLocationException if offset is outside the bounds of the 
 228:    *         content.
 229:    */
 230:   public Position createPosition(int offset) throws BadLocationException
 231:   {
 232:     if (offset < this.count || offset > this.count)
 233:       checkLocation(offset, 0);
 234:     StickyPosition sp = new StickyPosition(offset);
 235:     this.positions.add(sp);
 236:     return sp;
 237:   }
 238:   
 239:   /**
 240:    * Returns the length of the string content, including the '\n' character at
 241:    * the end.
 242:    * 
 243:    * @return The length of the string content.
 244:    */
 245:   public int length()
 246:   {
 247:     return this.count;
 248:   }
 249:   
 250:   /**
 251:    * Inserts <code>str</code> at the given position and returns an 
 252:    * {@link UndoableEdit} that enables undo/redo support.
 253:    * 
 254:    * @param where  the insertion point (must be less than 
 255:    *               <code>length()</code>).
 256:    * @param str  the string to insert (<code>null</code> not permitted).
 257:    * 
 258:    * @return An object that can undo the insertion.
 259:    */
 260:   public UndoableEdit insertString(int where, String str)
 261:     throws BadLocationException
 262:   {
 263:     checkLocation(where, 0);
 264:     if (where == this.count)
 265:       throw new BadLocationException("Invalid location", 1);
 266:     if (str == null)
 267:       throw new NullPointerException();
 268:     char[] insert = str.toCharArray();
 269:     char[] temp = new char[this.content.length + insert.length];
 270:     this.count += insert.length;
 271:     // Copy array and insert the string.
 272:     if (where > 0)
 273:       System.arraycopy(this.content, 0, temp, 0, where);
 274:     System.arraycopy(insert, 0, temp, where, insert.length);
 275:     System.arraycopy(this.content, where, temp, (where + insert.length), 
 276:         (temp.length - where - insert.length));
 277:     if (this.content.length < temp.length)
 278:       this.content = new char[temp.length];
 279:     // Copy the result in the original char array.
 280:     System.arraycopy(temp, 0, this.content, 0, temp.length);
 281:     // Move all the positions.
 282:     Vector refPos = getPositionsInRange(this.positions, where, 
 283:                                         temp.length - where);
 284:     Iterator iter = refPos.iterator();
 285:     while (iter.hasNext())
 286:       {
 287:         StickyPosition p = (StickyPosition)iter.next();
 288:         p.setOffset(p.getOffset() + str.length());
 289:       }
 290:     InsertUndo iundo = new InsertUndo(where, insert.length);
 291:     return iundo;
 292:   }
 293:   
 294:   /**
 295:    * Removes the specified range of characters and returns an 
 296:    * {@link UndoableEdit} that enables undo/redo support.
 297:    * 
 298:    * @param where  the starting index.
 299:    * @param nitems  the number of characters.
 300:    * 
 301:    * @return An object that can undo the removal.
 302:    * 
 303:    * @throws BadLocationException if the character range extends outside the
 304:    *         bounds of the content OR includes the last character.
 305:    */
 306:   public UndoableEdit remove(int where, int nitems) throws BadLocationException
 307:   {
 308:     checkLocation(where, nitems + 1);
 309:     char[] temp = new char[(this.content.length - nitems)];
 310:     this.count = this.count - nitems;
 311:     RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where, 
 312:         nitems));
 313:     // Copy array.
 314:     System.arraycopy(this.content, 0, temp, 0, where);
 315:     System.arraycopy(this.content, where + nitems, temp, where, 
 316:         this.content.length - where - nitems);
 317:     this.content = new char[temp.length];
 318:     // Then copy the result in the original char array.
 319:     System.arraycopy(temp, 0, this.content, 0, this.content.length);
 320:     // Move all the positions.
 321:     Vector refPos = getPositionsInRange(this.positions, where, 
 322:         this.content.length + nitems - where);
 323:     Iterator iter = refPos.iterator();
 324:     while (iter.hasNext())
 325:       {
 326:         StickyPosition p = (StickyPosition)iter.next();
 327:         int result = p.getOffset() - nitems;
 328:         p.setOffset(result);
 329:         if (result < 0)
 330:           this.positions.remove(p);
 331:       }
 332:     return rundo;
 333:   }
 334:   
 335:   /**
 336:    * Returns a new <code>String</code> containing the characters in the 
 337:    * specified range.
 338:    * 
 339:    * @param where  the start index.
 340:    * @param len  the number of characters.
 341:    * 
 342:    * @return A string.
 343:    * 
 344:    * @throws BadLocationException if the requested range of characters extends 
 345:    *         outside the bounds of the content.
 346:    */
 347:   public String getString(int where, int len) throws BadLocationException
 348:   {
 349:     checkLocation(where, len);
 350:     return new String(this.content, where, len);
 351:   }
 352:   
 353:   /**
 354:    * Updates <code>txt</code> to contain a direct reference to the underlying 
 355:    * character array.
 356:    * 
 357:    * @param where  the index of the first character.
 358:    * @param len  the number of characters.
 359:    * @param txt  a carrier for the return result (<code>null</code> not 
 360:    *             permitted).
 361:    *             
 362:    * @throws BadLocationException if the requested character range is not 
 363:    *                              within the bounds of the content.
 364:    * @throws NullPointerException if <code>txt</code> is <code>null</code>.
 365:    */
 366:   public void getChars(int where, int len, Segment txt) 
 367:     throws BadLocationException
 368:   {
 369:     checkLocation(where, len);
 370:     txt.array = this.content;
 371:     txt.offset = where;
 372:     txt.count = len;
 373:   }
 374: 
 375: 
 376:   /**
 377:    * @specnote This method is not very well specified and the positions vector
 378:    *           is implementation specific. The undo positions are managed
 379:    *           differently in this implementation, this method is only here
 380:    *           for binary compatibility.
 381:    */
 382:   protected void updateUndoPositions(Vector positions)
 383:   {
 384:     // We do nothing here.
 385:   }
 386: 
 387:   /** 
 388:    * A utility method that checks the validity of the specified character
 389:    * range.
 390:    * 
 391:    * @param where  the first character in the range.
 392:    * @param len  the number of characters in the range.
 393:    * 
 394:    * @throws BadLocationException if the specified range is not within the
 395:    *         bounds of the content.
 396:    */
 397:   void checkLocation(int where, int len) throws BadLocationException
 398:   {
 399:     if (where < 0)
 400:       throw new BadLocationException("Invalid location", 1);
 401:     else if (where > this.count)
 402:       throw new BadLocationException("Invalid location", this.count);
 403:     else if ((where + len) > this.count)
 404:       throw new BadLocationException("Invalid range", this.count);
 405:   }
 406:   
 407: }