Home » openjdk-7 » javax » swing » text » [javadoc | source]

    1   /*
    2    * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Oracle designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Oracle in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   22    * or visit www.oracle.com if you need additional information or have any
   23    * questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.util.Vector;
   28   import java.io.Serializable;
   29   import javax.swing.undo;
   30   import javax.swing.SwingUtilities;
   31   
   32   /**
   33    * An implementation of the AbstractDocument.Content interface that is
   34    * a brute force implementation that is useful for relatively small
   35    * documents and/or debugging.  It manages the character content
   36    * as a simple character array.  It is also quite inefficient.
   37    * <p>
   38    * It is generally recommended that the gap buffer or piece table
   39    * implementations be used instead.  This buffer does not scale up
   40    * to large sizes.
   41    * <p>
   42    * <strong>Warning:</strong>
   43    * Serialized objects of this class will not be compatible with
   44    * future Swing releases. The current serialization support is
   45    * appropriate for short term storage or RMI between applications running
   46    * the same version of Swing.  As of 1.4, support for long term storage
   47    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   48    * has been added to the <code>java.beans</code> package.
   49    * Please see {@link java.beans.XMLEncoder}.
   50    *
   51    * @author  Timothy Prinzing
   52    */
   53   public final class StringContent implements AbstractDocument.Content, Serializable {
   54   
   55       /**
   56        * Creates a new StringContent object.  Initial size defaults to 10.
   57        */
   58       public StringContent() {
   59           this(10);
   60       }
   61   
   62       /**
   63        * Creates a new StringContent object, with the initial
   64        * size specified.  If the length is < 1, a size of 1 is used.
   65        *
   66        * @param initialLength the initial size
   67        */
   68       public StringContent(int initialLength) {
   69           if (initialLength < 1) {
   70               initialLength = 1;
   71           }
   72           data = new char[initialLength];
   73           data[0] = '\n';
   74           count = 1;
   75       }
   76   
   77       /**
   78        * Returns the length of the content.
   79        *
   80        * @return the length >= 1
   81        * @see AbstractDocument.Content#length
   82        */
   83       public int length() {
   84           return count;
   85       }
   86   
   87       /**
   88        * Inserts a string into the content.
   89        *
   90        * @param where the starting position >= 0 && < length()
   91        * @param str the non-null string to insert
   92        * @return an UndoableEdit object for undoing
   93        * @exception BadLocationException if the specified position is invalid
   94        * @see AbstractDocument.Content#insertString
   95        */
   96       public UndoableEdit insertString(int where, String str) throws BadLocationException {
   97           if (where >= count || where < 0) {
   98               throw new BadLocationException("Invalid location", count);
   99           }
  100           char[] chars = str.toCharArray();
  101           replace(where, 0, chars, 0, chars.length);
  102           if (marks != null) {
  103               updateMarksForInsert(where, str.length());
  104           }
  105           return new InsertUndo(where, str.length());
  106       }
  107   
  108       /**
  109        * Removes part of the content.  where + nitems must be < length().
  110        *
  111        * @param where the starting position >= 0
  112        * @param nitems the number of characters to remove >= 0
  113        * @return an UndoableEdit object for undoing
  114        * @exception BadLocationException if the specified position is invalid
  115        * @see AbstractDocument.Content#remove
  116        */
  117       public UndoableEdit remove(int where, int nitems) throws BadLocationException {
  118           if (where + nitems >= count) {
  119               throw new BadLocationException("Invalid range", count);
  120           }
  121           String removedString = getString(where, nitems);
  122           UndoableEdit edit = new RemoveUndo(where, removedString);
  123           replace(where, nitems, empty, 0, 0);
  124           if (marks != null) {
  125               updateMarksForRemove(where, nitems);
  126           }
  127           return edit;
  128   
  129       }
  130   
  131       /**
  132        * Retrieves a portion of the content.  where + len must be <= length().
  133        *
  134        * @param where the starting position >= 0
  135        * @param len the length to retrieve >= 0
  136        * @return a string representing the content; may be empty
  137        * @exception BadLocationException if the specified position is invalid
  138        * @see AbstractDocument.Content#getString
  139        */
  140       public String getString(int where, int len) throws BadLocationException {
  141           if (where + len > count) {
  142               throw new BadLocationException("Invalid range", count);
  143           }
  144           return new String(data, where, len);
  145       }
  146   
  147       /**
  148        * Retrieves a portion of the content.  where + len must be <= length()
  149        *
  150        * @param where the starting position >= 0
  151        * @param len the number of characters to retrieve >= 0
  152        * @param chars the Segment object to return the characters in
  153        * @exception BadLocationException if the specified position is invalid
  154        * @see AbstractDocument.Content#getChars
  155        */
  156       public void getChars(int where, int len, Segment chars) throws BadLocationException {
  157           if (where + len > count) {
  158               throw new BadLocationException("Invalid location", count);
  159           }
  160           chars.array = data;
  161           chars.offset = where;
  162           chars.count = len;
  163       }
  164   
  165       /**
  166        * Creates a position within the content that will
  167        * track change as the content is mutated.
  168        *
  169        * @param offset the offset to create a position for >= 0
  170        * @return the position
  171        * @exception BadLocationException if the specified position is invalid
  172        */
  173       public Position createPosition(int offset) throws BadLocationException {
  174           // some small documents won't have any sticky positions
  175           // at all, so the buffer is created lazily.
  176           if (marks == null) {
  177               marks = new Vector<PosRec>();
  178           }
  179           return new StickyPosition(offset);
  180       }
  181   
  182       // --- local methods ---------------------------------------
  183   
  184       /**
  185        * Replaces some of the characters in the array
  186        * @param offset  offset into the array to start the replace
  187        * @param length  number of characters to remove
  188        * @param replArray replacement array
  189        * @param replOffset offset into the replacement array
  190        * @param replLength number of character to use from the
  191        *   replacement array.
  192        */
  193       void replace(int offset, int length,
  194                    char[] replArray, int replOffset, int replLength) {
  195           int delta = replLength - length;
  196           int src = offset + length;
  197           int nmove = count - src;
  198           int dest = src + delta;
  199           if ((count + delta) >= data.length) {
  200               // need to grow the array
  201               int newLength = Math.max(2*data.length, count + delta);
  202               char[] newData = new char[newLength];
  203               System.arraycopy(data, 0, newData, 0, offset);
  204               System.arraycopy(replArray, replOffset, newData, offset, replLength);
  205               System.arraycopy(data, src, newData, dest, nmove);
  206               data = newData;
  207           } else {
  208               // patch the existing array
  209               System.arraycopy(data, src, data, dest, nmove);
  210               System.arraycopy(replArray, replOffset, data, offset, replLength);
  211           }
  212           count = count + delta;
  213       }
  214   
  215       void resize(int ncount) {
  216           char[] ndata = new char[ncount];
  217           System.arraycopy(data, 0, ndata, 0, Math.min(ncount, count));
  218           data = ndata;
  219       }
  220   
  221       synchronized void updateMarksForInsert(int offset, int length) {
  222           if (offset == 0) {
  223               // zero is a special case where we update only
  224               // marks after it.
  225               offset = 1;
  226           }
  227           int n = marks.size();
  228           for (int i = 0; i < n; i++) {
  229               PosRec mark = marks.elementAt(i);
  230               if (mark.unused) {
  231                   // this record is no longer used, get rid of it
  232                   marks.removeElementAt(i);
  233                   i -= 1;
  234                   n -= 1;
  235               } else if (mark.offset >= offset) {
  236                   mark.offset += length;
  237               }
  238           }
  239       }
  240   
  241       synchronized void updateMarksForRemove(int offset, int length) {
  242           int n = marks.size();
  243           for (int i = 0; i < n; i++) {
  244               PosRec mark = marks.elementAt(i);
  245               if (mark.unused) {
  246                   // this record is no longer used, get rid of it
  247                   marks.removeElementAt(i);
  248                   i -= 1;
  249                   n -= 1;
  250               } else if (mark.offset >= (offset + length)) {
  251                   mark.offset -= length;
  252               } else if (mark.offset >= offset) {
  253                   mark.offset = offset;
  254               }
  255           }
  256       }
  257   
  258       /**
  259        * Returns a Vector containing instances of UndoPosRef for the
  260        * Positions in the range
  261        * <code>offset</code> to <code>offset</code> + <code>length</code>.
  262        * If <code>v</code> is not null the matching Positions are placed in
  263        * there. The vector with the resulting Positions are returned.
  264        * <p>
  265        * This is meant for internal usage, and is generally not of interest
  266        * to subclasses.
  267        *
  268        * @param v the Vector to use, with a new one created on null
  269        * @param offset the starting offset >= 0
  270        * @param length the length >= 0
  271        * @return the set of instances
  272        */
  273       protected Vector getPositionsInRange(Vector v, int offset,
  274                                                         int length) {
  275           int n = marks.size();
  276           int end = offset + length;
  277           Vector placeIn = (v == null) ? new Vector() : v;
  278           for (int i = 0; i < n; i++) {
  279               PosRec mark = marks.elementAt(i);
  280               if (mark.unused) {
  281                   // this record is no longer used, get rid of it
  282                   marks.removeElementAt(i);
  283                   i -= 1;
  284                   n -= 1;
  285               } else if(mark.offset >= offset && mark.offset <= end)
  286                   placeIn.addElement(new UndoPosRef(mark));
  287           }
  288           return placeIn;
  289       }
  290   
  291       /**
  292        * Resets the location for all the UndoPosRef instances
  293        * in <code>positions</code>.
  294        * <p>
  295        * This is meant for internal usage, and is generally not of interest
  296        * to subclasses.
  297        *
  298        * @param positions the positions of the instances
  299        */
  300       protected void updateUndoPositions(Vector positions) {
  301           for(int counter = positions.size() - 1; counter >= 0; counter--) {
  302               UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
  303               // Check if the Position is still valid.
  304               if(ref.rec.unused) {
  305                   positions.removeElementAt(counter);
  306               }
  307               else
  308                   ref.resetLocation();
  309           }
  310       }
  311   
  312       private static final char[] empty = new char[0];
  313       private char[] data;
  314       private int count;
  315       transient Vector<PosRec> marks;
  316   
  317       /**
  318        * holds the data for a mark... separately from
  319        * the real mark so that the real mark can be
  320        * collected if there are no more references to
  321        * it.... the update table holds only a reference
  322        * to this grungy thing.
  323        */
  324       final class PosRec {
  325   
  326           PosRec(int offset) {
  327               this.offset = offset;
  328           }
  329   
  330           int offset;
  331           boolean unused;
  332       }
  333   
  334       /**
  335        * This really wants to be a weak reference but
  336        * in 1.1 we don't have a 100% pure solution for
  337        * this... so this class trys to hack a solution
  338        * to causing the marks to be collected.
  339        */
  340       final class StickyPosition implements Position {
  341   
  342           StickyPosition(int offset) {
  343               rec = new PosRec(offset);
  344               marks.addElement(rec);
  345           }
  346   
  347           public int getOffset() {
  348               return rec.offset;
  349           }
  350   
  351           protected void finalize() throws Throwable {
  352               // schedule the record to be removed later
  353               // on another thread.
  354               rec.unused = true;
  355           }
  356   
  357           public String toString() {
  358               return Integer.toString(getOffset());
  359           }
  360   
  361           PosRec rec;
  362       }
  363   
  364       /**
  365        * Used to hold a reference to a Position that is being reset as the
  366        * result of removing from the content.
  367        */
  368       final class UndoPosRef {
  369           UndoPosRef(PosRec rec) {
  370               this.rec = rec;
  371               this.undoLocation = rec.offset;
  372           }
  373   
  374           /**
  375            * Resets the location of the Position to the offset when the
  376            * receiver was instantiated.
  377            */
  378           protected void resetLocation() {
  379               rec.offset = undoLocation;
  380           }
  381   
  382           /** Location to reset to when resetLocatino is invoked. */
  383           protected int undoLocation;
  384           /** Position to reset offset. */
  385           protected PosRec rec;
  386       }
  387   
  388       /**
  389        * UnoableEdit created for inserts.
  390        */
  391       class InsertUndo extends AbstractUndoableEdit {
  392           protected InsertUndo(int offset, int length) {
  393               super();
  394               this.offset = offset;
  395               this.length = length;
  396           }
  397   
  398           public void undo() throws CannotUndoException {
  399               super.undo();
  400               try {
  401                   synchronized(StringContent.this) {
  402                       // Get the Positions in the range being removed.
  403                       if(marks != null)
  404                           posRefs = getPositionsInRange(null, offset, length);
  405                       string = getString(offset, length);
  406                       remove(offset, length);
  407                   }
  408               } catch (BadLocationException bl) {
  409                 throw new CannotUndoException();
  410               }
  411           }
  412   
  413           public void redo() throws CannotRedoException {
  414               super.redo();
  415               try {
  416                   synchronized(StringContent.this) {
  417                       insertString(offset, string);
  418                       string = null;
  419                       // Update the Positions that were in the range removed.
  420                       if(posRefs != null) {
  421                           updateUndoPositions(posRefs);
  422                           posRefs = null;
  423                       }
  424                 }
  425               } catch (BadLocationException bl) {
  426                 throw new CannotRedoException();
  427               }
  428           }
  429   
  430           // Where the string goes.
  431           protected int offset;
  432           // Length of the string.
  433           protected int length;
  434           // The string that was inserted. To cut down on space needed this
  435           // will only be valid after an undo.
  436           protected String string;
  437           // An array of instances of UndoPosRef for the Positions in the
  438           // range that was removed, valid after undo.
  439           protected Vector posRefs;
  440       }
  441   
  442   
  443       /**
  444        * UndoableEdit created for removes.
  445        */
  446       class RemoveUndo extends AbstractUndoableEdit {
  447           protected RemoveUndo(int offset, String string) {
  448               super();
  449               this.offset = offset;
  450               this.string = string;
  451               this.length = string.length();
  452               if(marks != null)
  453                   posRefs = getPositionsInRange(null, offset, length);
  454           }
  455   
  456           public void undo() throws CannotUndoException {
  457               super.undo();
  458               try {
  459                   synchronized(StringContent.this) {
  460                       insertString(offset, string);
  461                       // Update the Positions that were in the range removed.
  462                       if(posRefs != null) {
  463                           updateUndoPositions(posRefs);
  464                           posRefs = null;
  465                       }
  466                       string = null;
  467                   }
  468               } catch (BadLocationException bl) {
  469                 throw new CannotUndoException();
  470               }
  471           }
  472   
  473           public void redo() throws CannotRedoException {
  474               super.redo();
  475               try {
  476                   synchronized(StringContent.this) {
  477                       string = getString(offset, length);
  478                       // Get the Positions in the range being removed.
  479                       if(marks != null)
  480                           posRefs = getPositionsInRange(null, offset, length);
  481                       remove(offset, length);
  482                   }
  483               } catch (BadLocationException bl) {
  484                 throw new CannotRedoException();
  485               }
  486           }
  487   
  488           // Where the string goes.
  489           protected int offset;
  490           // Length of the string.
  491           protected int length;
  492           // The string that was inserted. This will be null after an undo.
  493           protected String string;
  494           // An array of instances of UndoPosRef for the Positions in the
  495           // range that was removed, valid before undo.
  496           protected Vector posRefs;
  497       }
  498   }

Home » openjdk-7 » javax » swing » text » [javadoc | source]