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".  This is equivalent
 182:    * to calling {@link #StringContent(int)} with an <code>initialLength</code>
 183:    * of 10.
 184:    */
 185:   public StringContent()
 186:   {
 187:     this(10);
 188:   }
 189: 
 190:   /**
 191:    * Creates a new instance containing the string "\n".
 192:    * 
 193:    * @param initialLength  the initial length of the underlying character 
 194:    *                       array used to store the content.
 195:    */
 196:   public StringContent(int initialLength)
 197:   {
 198:     super();
 199:     if (initialLength < 1)
 200:       initialLength = 1;
 201:     this.content = new char[initialLength];
 202:     this.content[0] = '\n';
 203:     this.count = 1;
 204:   }
 205: 
 206:   protected Vector getPositionsInRange(Vector v,
 207:                                        int offset,
 208:                                        int length)
 209:   {
 210:     Vector refPos = new Vector();
 211:     Iterator iter = this.positions.iterator();
 212:     while(iter.hasNext())
 213:       {
 214:         Position p = (Position) iter.next();
 215:         if ((offset <= p.getOffset())
 216:             && (p.getOffset() <= (offset + length)))
 217:           refPos.add(p);
 218:       }
 219:     return refPos;
 220:   }
 221: 
 222:   /**
 223:    * Creates a position reference for the character at the given offset.  The
 224:    * position offset will be automatically updated when new characters are
 225:    * inserted into or removed from the content.
 226:    * 
 227:    * @param offset  the character offset.
 228:    * 
 229:    * @throws BadLocationException if offset is outside the bounds of the 
 230:    *         content.
 231:    */
 232:   public Position createPosition(int offset) throws BadLocationException
 233:   {
 234:     if (offset < this.count || offset > this.count)
 235:       checkLocation(offset, 0);
 236:     StickyPosition sp = new StickyPosition(offset);
 237:     this.positions.add(sp);
 238:     return sp;
 239:   }
 240:   
 241:   /**
 242:    * Returns the length of the string content, including the '\n' character at
 243:    * the end.
 244:    * 
 245:    * @return The length of the string content.
 246:    */
 247:   public int length()
 248:   {
 249:     return this.count;
 250:   }
 251:   
 252:   /**
 253:    * Inserts <code>str</code> at the given position and returns an 
 254:    * {@link UndoableEdit} that enables undo/redo support.
 255:    * 
 256:    * @param where  the insertion point (must be less than 
 257:    *               <code>length()</code>).
 258:    * @param str  the string to insert (<code>null</code> not permitted).
 259:    * 
 260:    * @return An object that can undo the insertion.
 261:    */
 262:   public UndoableEdit insertString(int where, String str)
 263:     throws BadLocationException
 264:   {
 265:     checkLocation(where, 0);
 266:     if (where == this.count)
 267:       throw new BadLocationException("Invalid location", 1);
 268:     if (str == null)
 269:       throw new NullPointerException();
 270:     char[] insert = str.toCharArray();
 271:     char[] temp = new char[this.content.length + insert.length];
 272:     this.count += insert.length;
 273:     // Copy array and insert the string.
 274:     if (where > 0)
 275:       System.arraycopy(this.content, 0, temp, 0, where);
 276:     System.arraycopy(insert, 0, temp, where, insert.length);
 277:     System.arraycopy(this.content, where, temp, (where + insert.length), 
 278:         (temp.length - where - insert.length));
 279:     if (this.content.length < temp.length)
 280:       this.content = new char[temp.length];
 281:     // Copy the result in the original char array.
 282:     System.arraycopy(temp, 0, this.content, 0, temp.length);
 283:     // Move all the positions.
 284:     Vector refPos = getPositionsInRange(this.positions, where, 
 285:                                         temp.length - where);
 286:     Iterator iter = refPos.iterator();
 287:     while (iter.hasNext())
 288:       {
 289:         StickyPosition p = (StickyPosition)iter.next();
 290:         p.setOffset(p.getOffset() + str.length());
 291:       }
 292:     InsertUndo iundo = new InsertUndo(where, insert.length);
 293:     return iundo;
 294:   }
 295:   
 296:   /**
 297:    * Removes the specified range of characters and returns an 
 298:    * {@link UndoableEdit} that enables undo/redo support.
 299:    * 
 300:    * @param where  the starting index.
 301:    * @param nitems  the number of characters.
 302:    * 
 303:    * @return An object that can undo the removal.
 304:    * 
 305:    * @throws BadLocationException if the character range extends outside the
 306:    *         bounds of the content OR includes the last character.
 307:    */
 308:   public UndoableEdit remove(int where, int nitems) throws BadLocationException
 309:   {
 310:     checkLocation(where, nitems + 1);
 311:     char[] temp = new char[(this.content.length - nitems)];
 312:     this.count = this.count - nitems;
 313:     RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where, 
 314:         nitems));
 315:     // Copy array.
 316:     System.arraycopy(this.content, 0, temp, 0, where);
 317:     System.arraycopy(this.content, where + nitems, temp, where, 
 318:         this.content.length - where - nitems);
 319:     this.content = new char[temp.length];
 320:     // Then copy the result in the original char array.
 321:     System.arraycopy(temp, 0, this.content, 0, this.content.length);
 322:     // Move all the positions.
 323:     Vector refPos = getPositionsInRange(this.positions, where, 
 324:         this.content.length + nitems - where);
 325:     Iterator iter = refPos.iterator();
 326:     while (iter.hasNext())
 327:       {
 328:         StickyPosition p = (StickyPosition)iter.next();
 329:         int result = p.getOffset() - nitems;
 330:         p.setOffset(result);
 331:         if (result < 0)
 332:           this.positions.remove(p);
 333:       }
 334:     return rundo;
 335:   }
 336:   
 337:   /**
 338:    * Returns a new <code>String</code> containing the characters in the 
 339:    * specified range.
 340:    * 
 341:    * @param where  the start index.
 342:    * @param len  the number of characters.
 343:    * 
 344:    * @return A string.
 345:    * 
 346:    * @throws BadLocationException if the requested range of characters extends 
 347:    *         outside the bounds of the content.
 348:    */
 349:   public String getString(int where, int len) throws BadLocationException
 350:   {
 351:     checkLocation(where, len);
 352:     return new String(this.content, where, len);
 353:   }
 354:   
 355:   /**
 356:    * Updates <code>txt</code> to contain a direct reference to the underlying 
 357:    * character array.
 358:    * 
 359:    * @param where  the index of the first character.
 360:    * @param len  the number of characters.
 361:    * @param txt  a carrier for the return result (<code>null</code> not 
 362:    *             permitted).
 363:    *             
 364:    * @throws BadLocationException if the requested character range is not 
 365:    *                              within the bounds of the content.
 366:    * @throws NullPointerException if <code>txt</code> is <code>null</code>.
 367:    */
 368:   public void getChars(int where, int len, Segment txt) 
 369:     throws BadLocationException
 370:   {
 371:     checkLocation(where, len);
 372:     txt.array = this.content;
 373:     txt.offset = where;
 374:     txt.count = len;
 375:   }
 376: 
 377: 
 378:   /**
 379:    * @specnote This method is not very well specified and the positions vector
 380:    *           is implementation specific. The undo positions are managed
 381:    *           differently in this implementation, this method is only here
 382:    *           for binary compatibility.
 383:    */
 384:   protected void updateUndoPositions(Vector positions)
 385:   {
 386:     // We do nothing here.
 387:   }
 388: 
 389:   /** 
 390:    * A utility method that checks the validity of the specified character
 391:    * range.
 392:    * 
 393:    * @param where  the first character in the range.
 394:    * @param len  the number of characters in the range.
 395:    * 
 396:    * @throws BadLocationException if the specified range is not within the
 397:    *         bounds of the content.
 398:    */
 399:   void checkLocation(int where, int len) throws BadLocationException
 400:   {
 401:     if (where < 0)
 402:       throw new BadLocationException("Invalid location", 1);
 403:     else if (where > this.count)
 404:       throw new BadLocationException("Invalid location", this.count);
 405:     else if ((where + len) > this.count)
 406:       throw new BadLocationException("Invalid range", this.count);
 407:   }
 408:   
 409: }