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

    1   /*
    2    * Copyright (c) 1998, 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.IOException;
   29   import java.io.ObjectInputStream;
   30   import java.io.Serializable;
   31   import javax.swing.undo.AbstractUndoableEdit;
   32   import javax.swing.undo.CannotRedoException;
   33   import javax.swing.undo.CannotUndoException;
   34   import javax.swing.undo.UndoableEdit;
   35   import javax.swing.SwingUtilities;
   36   import java.lang.ref.WeakReference;
   37   import java.lang.ref.ReferenceQueue;
   38   
   39   /**
   40    * An implementation of the AbstractDocument.Content interface
   41    * implemented using a gapped buffer similar to that used by emacs.
   42    * The underlying storage is a array of unicode characters with
   43    * a gap somewhere.  The gap is moved to the location of changes
   44    * to take advantage of common behavior where most changes are
   45    * in the same location.  Changes that occur at a gap boundary are
   46    * generally cheap and moving the gap is generally cheaper than
   47    * moving the array contents directly to accomodate the change.
   48    * <p>
   49    * The positions tracking change are also generally cheap to
   50    * maintain.  The Position implementations (marks) store the array
   51    * index and can easily calculate the sequential position from
   52    * the current gap location.  Changes only require update to the
   53    * the marks between the old and new gap boundaries when the gap
   54    * is moved, so generally updating the marks is pretty cheap.
   55    * The marks are stored sorted so they can be located quickly
   56    * with a binary search.  This increases the cost of adding a
   57    * mark, and decreases the cost of keeping the mark updated.
   58    *
   59    * @author  Timothy Prinzing
   60    */
   61   public class GapContent extends GapVector implements AbstractDocument.Content, Serializable {
   62   
   63       /**
   64        * Creates a new GapContent object.  Initial size defaults to 10.
   65        */
   66       public GapContent() {
   67           this(10);
   68       }
   69   
   70       /**
   71        * Creates a new GapContent object, with the initial
   72        * size specified.  The initial size will not be allowed
   73        * to go below 2, to give room for the implied break and
   74        * the gap.
   75        *
   76        * @param initialLength the initial size
   77        */
   78       public GapContent(int initialLength) {
   79           super(Math.max(initialLength,2));
   80           char[] implied = new char[1];
   81           implied[0] = '\n';
   82           replace(0, 0, implied, implied.length);
   83   
   84           marks = new MarkVector();
   85           search = new MarkData(0);
   86           queue = new ReferenceQueue<StickyPosition>();
   87       }
   88   
   89       /**
   90        * Allocate an array to store items of the type
   91        * appropriate (which is determined by the subclass).
   92        */
   93       protected Object allocateArray(int len) {
   94           return new char[len];
   95       }
   96   
   97       /**
   98        * Get the length of the allocated array.
   99        */
  100       protected int getArrayLength() {
  101           char[] carray = (char[]) getArray();
  102           return carray.length;
  103       }
  104   
  105       // --- AbstractDocument.Content methods -------------------------
  106   
  107       /**
  108        * Returns the length of the content.
  109        *
  110        * @return the length >= 1
  111        * @see AbstractDocument.Content#length
  112        */
  113       public int length() {
  114           int len = getArrayLength() - (getGapEnd() - getGapStart());
  115           return len;
  116       }
  117   
  118       /**
  119        * Inserts a string into the content.
  120        *
  121        * @param where the starting position >= 0, < length()
  122        * @param str the non-null string to insert
  123        * @return an UndoableEdit object for undoing
  124        * @exception BadLocationException if the specified position is invalid
  125        * @see AbstractDocument.Content#insertString
  126        */
  127       public UndoableEdit insertString(int where, String str) throws BadLocationException {
  128           if (where > length() || where < 0) {
  129               throw new BadLocationException("Invalid insert", length());
  130           }
  131           char[] chars = str.toCharArray();
  132           replace(where, 0, chars, chars.length);
  133           return new InsertUndo(where, str.length());
  134       }
  135   
  136       /**
  137        * Removes part of the content.
  138        *
  139        * @param where the starting position >= 0, where + nitems < length()
  140        * @param nitems the number of characters to remove >= 0
  141        * @return an UndoableEdit object for undoing
  142        * @exception BadLocationException if the specified position is invalid
  143        * @see AbstractDocument.Content#remove
  144        */
  145       public UndoableEdit remove(int where, int nitems) throws BadLocationException {
  146           if (where + nitems >= length()) {
  147               throw new BadLocationException("Invalid remove", length() + 1);
  148           }
  149           String removedString = getString(where, nitems);
  150           UndoableEdit edit = new RemoveUndo(where, removedString);
  151           replace(where, nitems, empty, 0);
  152           return edit;
  153   
  154       }
  155   
  156       /**
  157        * Retrieves a portion of the content.
  158        *
  159        * @param where the starting position >= 0
  160        * @param len the length to retrieve >= 0
  161        * @return a string representing the content
  162        * @exception BadLocationException if the specified position is invalid
  163        * @see AbstractDocument.Content#getString
  164        */
  165       public String getString(int where, int len) throws BadLocationException {
  166           Segment s = new Segment();
  167           getChars(where, len, s);
  168           return new String(s.array, s.offset, s.count);
  169       }
  170   
  171       /**
  172        * Retrieves a portion of the content.  If the desired content spans
  173        * the gap, we copy the content.  If the desired content does not
  174        * span the gap, the actual store is returned to avoid the copy since
  175        * it is contiguous.
  176        *
  177        * @param where the starting position >= 0, where + len <= length()
  178        * @param len the number of characters to retrieve >= 0
  179        * @param chars the Segment object to return the characters in
  180        * @exception BadLocationException if the specified position is invalid
  181        * @see AbstractDocument.Content#getChars
  182        */
  183       public void getChars(int where, int len, Segment chars) throws BadLocationException {
  184           int end = where + len;
  185           if (where < 0 || end < 0) {
  186               throw new BadLocationException("Invalid location", -1);
  187           }
  188           if (end > length() || where > length()) {
  189               throw new BadLocationException("Invalid location", length() + 1);
  190           }
  191           int g0 = getGapStart();
  192           int g1 = getGapEnd();
  193           char[] array = (char[]) getArray();
  194           if ((where + len) <= g0) {
  195               // below gap
  196               chars.array = array;
  197               chars.offset = where;
  198           } else if (where >= g0) {
  199               // above gap
  200               chars.array = array;
  201               chars.offset = g1 + where - g0;
  202           } else {
  203               // spans the gap
  204               int before = g0 - where;
  205               if (chars.isPartialReturn()) {
  206                   // partial return allowed, return amount before the gap
  207                   chars.array = array;
  208                   chars.offset = where;
  209                   chars.count = before;
  210                   return;
  211               }
  212               // partial return not allowed, must copy
  213               chars.array = new char[len];
  214               chars.offset = 0;
  215               System.arraycopy(array, where, chars.array, 0, before);
  216               System.arraycopy(array, g1, chars.array, before, len - before);
  217           }
  218           chars.count = len;
  219       }
  220   
  221       /**
  222        * Creates a position within the content that will
  223        * track change as the content is mutated.
  224        *
  225        * @param offset the offset to track >= 0
  226        * @return the position
  227        * @exception BadLocationException if the specified position is invalid
  228        */
  229       public Position createPosition(int offset) throws BadLocationException {
  230           while ( queue.poll() != null ) {
  231               unusedMarks++;
  232           }
  233           if (unusedMarks > Math.max(5, (marks.size() / 10))) {
  234               removeUnusedMarks();
  235           }
  236           int g0 = getGapStart();
  237           int g1 = getGapEnd();
  238           int index = (offset < g0) ? offset : offset + (g1 - g0);
  239           search.index = index;
  240           int sortIndex = findSortIndex(search);
  241           MarkData m;
  242           StickyPosition position;
  243           if (sortIndex < marks.size()
  244               && (m = marks.elementAt(sortIndex)).index == index
  245               && (position = m.getPosition()) != null) {
  246               //position references the correct StickyPostition
  247           } else {
  248               position = new StickyPosition();
  249               m = new MarkData(index,position,queue);
  250               position.setMark(m);
  251               marks.insertElementAt(m, sortIndex);
  252           }
  253   
  254           return position;
  255       }
  256   
  257       /**
  258        * Holds the data for a mark... separately from
  259        * the real mark so that the real mark (Position
  260        * that the caller of createPosition holds) can be
  261        * collected if there are no more references to
  262        * it.  The update table holds only a reference
  263        * to this data.
  264        */
  265       final class MarkData extends WeakReference<StickyPosition> {
  266   
  267           MarkData(int index) {
  268               super(null);
  269               this.index = index;
  270           }
  271           MarkData(int index, StickyPosition position, ReferenceQueue<? super StickyPosition> queue) {
  272               super(position, queue);
  273               this.index = index;
  274           }
  275   
  276           /**
  277            * Fetch the location in the contiguous sequence
  278            * being modeled.  The index in the gap array
  279            * is held by the mark, so it is adjusted according
  280            * to it's relationship to the gap.
  281            */
  282           public final int getOffset() {
  283               int g0 = getGapStart();
  284               int g1 = getGapEnd();
  285               int offs = (index < g0) ? index : index - (g1 - g0);
  286               return Math.max(offs, 0);
  287           }
  288   
  289           StickyPosition getPosition() {
  290               return get();
  291           }
  292           int index;
  293       }
  294   
  295       final class StickyPosition implements Position {
  296   
  297           StickyPosition() {
  298           }
  299   
  300           void setMark(MarkData mark) {
  301               this.mark = mark;
  302           }
  303   
  304           public final int getOffset() {
  305               return mark.getOffset();
  306           }
  307   
  308           public String toString() {
  309               return Integer.toString(getOffset());
  310           }
  311   
  312           MarkData mark;
  313       }
  314   
  315       // --- variables --------------------------------------
  316   
  317       private static final char[] empty = new char[0];
  318       private transient MarkVector marks;
  319   
  320       /**
  321        * Record used for searching for the place to
  322        * start updating mark indexs when the gap
  323        * boundaries are moved.
  324        */
  325       private transient MarkData search;
  326   
  327       /**
  328        * The number of unused mark entries
  329        */
  330       private transient int unusedMarks = 0;
  331   
  332       private transient ReferenceQueue<StickyPosition> queue;
  333   
  334       final static int GROWTH_SIZE = 1024 * 512;
  335   
  336       // --- gap management -------------------------------
  337   
  338       /**
  339        * Make the gap bigger, moving any necessary data and updating
  340        * the appropriate marks
  341        */
  342       protected void shiftEnd(int newSize) {
  343           int oldGapEnd = getGapEnd();
  344   
  345           super.shiftEnd(newSize);
  346   
  347           // Adjust marks.
  348           int dg = getGapEnd() - oldGapEnd;
  349           int adjustIndex = findMarkAdjustIndex(oldGapEnd);
  350           int n = marks.size();
  351           for (int i = adjustIndex; i < n; i++) {
  352               MarkData mark = marks.elementAt(i);
  353               mark.index += dg;
  354           }
  355       }
  356   
  357       /**
  358        * Overridden to make growth policy less agressive for large
  359        * text amount.
  360        */
  361       int getNewArraySize(int reqSize) {
  362           if (reqSize < GROWTH_SIZE) {
  363               return super.getNewArraySize(reqSize);
  364           } else {
  365               return reqSize + GROWTH_SIZE;
  366           }
  367       }
  368   
  369       /**
  370        * Move the start of the gap to a new location,
  371        * without changing the size of the gap.  This
  372        * moves the data in the array and updates the
  373        * marks accordingly.
  374        */
  375       protected void shiftGap(int newGapStart) {
  376           int oldGapStart = getGapStart();
  377           int dg = newGapStart - oldGapStart;
  378           int oldGapEnd = getGapEnd();
  379           int newGapEnd = oldGapEnd + dg;
  380           int gapSize = oldGapEnd - oldGapStart;
  381   
  382           // shift gap in the character array
  383           super.shiftGap(newGapStart);
  384   
  385           // update the marks
  386           if (dg > 0) {
  387               // Move gap up, move data and marks down.
  388               int adjustIndex = findMarkAdjustIndex(oldGapStart);
  389               int n = marks.size();
  390               for (int i = adjustIndex; i < n; i++) {
  391                   MarkData mark = marks.elementAt(i);
  392                   if (mark.index >= newGapEnd) {
  393                       break;
  394                   }
  395                   mark.index -= gapSize;
  396               }
  397           } else if (dg < 0) {
  398               // Move gap down, move data and marks up.
  399               int adjustIndex = findMarkAdjustIndex(newGapStart);
  400               int n = marks.size();
  401               for (int i = adjustIndex; i < n; i++) {
  402                   MarkData mark = marks.elementAt(i);
  403                   if (mark.index >= oldGapEnd) {
  404                       break;
  405                   }
  406                   mark.index += gapSize;
  407               }
  408           }
  409           resetMarksAtZero();
  410       }
  411   
  412       /**
  413        * Resets all the marks that have an offset of 0 to have an index of
  414        * zero as well.
  415        */
  416       protected void resetMarksAtZero() {
  417           if (marks != null && getGapStart() == 0) {
  418               int g1 = getGapEnd();
  419               for (int counter = 0, maxCounter = marks.size();
  420                    counter < maxCounter; counter++) {
  421                   MarkData mark = marks.elementAt(counter);
  422                   if (mark.index <= g1) {
  423                       mark.index = 0;
  424                   }
  425                   else {
  426                       break;
  427                   }
  428               }
  429           }
  430       }
  431   
  432       /**
  433        * Adjust the gap end downward.  This doesn't move
  434        * any data, but it does update any marks affected
  435        * by the boundary change.  All marks from the old
  436        * gap start down to the new gap start are squeezed
  437        * to the end of the gap (their location has been
  438        * removed).
  439        */
  440       protected void shiftGapStartDown(int newGapStart) {
  441           // Push aside all marks from oldGapStart down to newGapStart.
  442           int adjustIndex = findMarkAdjustIndex(newGapStart);
  443           int n = marks.size();
  444           int g0 = getGapStart();
  445           int g1 = getGapEnd();
  446           for (int i = adjustIndex; i < n; i++) {
  447               MarkData mark = marks.elementAt(i);
  448               if (mark.index > g0) {
  449                   // no more marks to adjust
  450                   break;
  451               }
  452               mark.index = g1;
  453           }
  454   
  455           // shift the gap in the character array
  456           super.shiftGapStartDown(newGapStart);
  457   
  458           resetMarksAtZero();
  459       }
  460   
  461       /**
  462        * Adjust the gap end upward.  This doesn't move
  463        * any data, but it does update any marks affected
  464        * by the boundary change. All marks from the old
  465        * gap end up to the new gap end are squeezed
  466        * to the end of the gap (their location has been
  467        * removed).
  468        */
  469       protected void shiftGapEndUp(int newGapEnd) {
  470           int adjustIndex = findMarkAdjustIndex(getGapEnd());
  471           int n = marks.size();
  472           for (int i = adjustIndex; i < n; i++) {
  473               MarkData mark = marks.elementAt(i);
  474               if (mark.index >= newGapEnd) {
  475                   break;
  476               }
  477               mark.index = newGapEnd;
  478           }
  479   
  480           // shift the gap in the character array
  481           super.shiftGapEndUp(newGapEnd);
  482   
  483           resetMarksAtZero();
  484       }
  485   
  486       /**
  487        * Compares two marks.
  488        *
  489        * @param o1 the first object
  490        * @param o2 the second object
  491        * @return < 0 if o1 < o2, 0 if the same, > 0 if o1 > o2
  492        */
  493       final int compare(MarkData o1, MarkData o2) {
  494           if (o1.index < o2.index) {
  495               return -1;
  496           } else if (o1.index > o2.index) {
  497               return 1;
  498           } else {
  499               return 0;
  500           }
  501       }
  502   
  503       /**
  504        * Finds the index to start mark adjustments given
  505        * some search index.
  506        */
  507       final int findMarkAdjustIndex(int searchIndex) {
  508           search.index = Math.max(searchIndex, 1);
  509           int index = findSortIndex(search);
  510   
  511           // return the first in the series
  512           // (ie. there may be duplicates).
  513           for (int i = index - 1; i >= 0; i--) {
  514               MarkData d = marks.elementAt(i);
  515               if (d.index != search.index) {
  516                   break;
  517               }
  518               index -= 1;
  519           }
  520           return index;
  521       }
  522   
  523       /**
  524        * Finds the index of where to insert a new mark.
  525        *
  526        * @param o the mark to insert
  527        * @return the index
  528        */
  529       final int findSortIndex(MarkData o) {
  530           int lower = 0;
  531           int upper = marks.size() - 1;
  532           int mid = 0;
  533   
  534           if (upper == -1) {
  535               return 0;
  536           }
  537   
  538           int cmp;
  539           MarkData last = marks.elementAt(upper);
  540           cmp = compare(o, last);
  541           if (cmp > 0)
  542               return upper + 1;
  543   
  544           while (lower <= upper) {
  545               mid = lower + ((upper - lower) / 2);
  546               MarkData entry = marks.elementAt(mid);
  547               cmp = compare(o, entry);
  548   
  549               if (cmp == 0) {
  550                   // found a match
  551                   return mid;
  552               } else if (cmp < 0) {
  553                   upper = mid - 1;
  554               } else {
  555                   lower = mid + 1;
  556               }
  557           }
  558   
  559           // didn't find it, but we indicate the index of where it would belong.
  560           return (cmp < 0) ? mid : mid + 1;
  561       }
  562   
  563       /**
  564        * Remove all unused marks out of the sorted collection
  565        * of marks.
  566        */
  567       final void removeUnusedMarks() {
  568           int n = marks.size();
  569           MarkVector cleaned = new MarkVector(n);
  570           for (int i = 0; i < n; i++) {
  571               MarkData mark = marks.elementAt(i);
  572               if (mark.get() != null) {
  573                   cleaned.addElement(mark);
  574               }
  575           }
  576           marks = cleaned;
  577           unusedMarks = 0;
  578       }
  579   
  580   
  581       static class MarkVector extends GapVector {
  582   
  583           MarkVector() {
  584               super();
  585           }
  586   
  587           MarkVector(int size) {
  588               super(size);
  589           }
  590   
  591           /**
  592            * Allocate an array to store items of the type
  593            * appropriate (which is determined by the subclass).
  594            */
  595           protected Object allocateArray(int len) {
  596               return new MarkData[len];
  597           }
  598   
  599           /**
  600            * Get the length of the allocated array
  601            */
  602           protected int getArrayLength() {
  603               MarkData[] marks = (MarkData[]) getArray();
  604               return marks.length;
  605           }
  606   
  607           /**
  608            * Returns the number of marks currently held
  609            */
  610           public int size() {
  611               int len = getArrayLength() - (getGapEnd() - getGapStart());
  612               return len;
  613           }
  614   
  615           /**
  616            * Inserts a mark into the vector
  617            */
  618           public void insertElementAt(MarkData m, int index) {
  619               oneMark[0] = m;
  620               replace(index, 0, oneMark, 1);
  621           }
  622   
  623           /**
  624            * Add a mark to the end
  625            */
  626           public void addElement(MarkData m) {
  627               insertElementAt(m, size());
  628           }
  629   
  630           /**
  631            * Fetches the mark at the given index
  632            */
  633           public MarkData elementAt(int index) {
  634               int g0 = getGapStart();
  635               int g1 = getGapEnd();
  636               MarkData[] array = (MarkData[]) getArray();
  637               if (index < g0) {
  638                   // below gap
  639                   return array[index];
  640               } else {
  641                   // above gap
  642                   index += g1 - g0;
  643                   return array[index];
  644               }
  645           }
  646   
  647           /**
  648            * Replaces the elements in the specified range with the passed
  649            * in objects. This will NOT adjust the gap. The passed in indices
  650            * do not account for the gap, they are the same as would be used
  651            * int <code>elementAt</code>.
  652            */
  653           protected void replaceRange(int start, int end, Object[] marks) {
  654               int g0 = getGapStart();
  655               int g1 = getGapEnd();
  656               int index = start;
  657               int newIndex = 0;
  658               Object[] array = (Object[]) getArray();
  659               if (start >= g0) {
  660                   // Completely passed gap
  661                   index += (g1 - g0);
  662                   end += (g1 - g0);
  663               }
  664               else if (end >= g0) {
  665                   // straddles gap
  666                   end += (g1 - g0);
  667                   while (index < g0) {
  668                       array[index++] = marks[newIndex++];
  669                   }
  670                   index = g1;
  671               }
  672               else {
  673                   // below gap
  674                   while (index < end) {
  675                       array[index++] = marks[newIndex++];
  676                   }
  677               }
  678               while (index < end) {
  679                   array[index++] = marks[newIndex++];
  680               }
  681           }
  682   
  683           MarkData[] oneMark = new MarkData[1];
  684   
  685       }
  686   
  687       // --- serialization -------------------------------------
  688   
  689       private void readObject(ObjectInputStream s)
  690         throws ClassNotFoundException, IOException {
  691           s.defaultReadObject();
  692           marks = new MarkVector();
  693           search = new MarkData(0);
  694           queue = new ReferenceQueue<StickyPosition>();
  695       }
  696   
  697   
  698       // --- undo support --------------------------------------
  699   
  700       /**
  701        * Returns a Vector containing instances of UndoPosRef for the
  702        * Positions in the range
  703        * <code>offset</code> to <code>offset</code> + <code>length</code>.
  704        * If <code>v</code> is not null the matching Positions are placed in
  705        * there. The vector with the resulting Positions are returned.
  706        *
  707        * @param v the Vector to use, with a new one created on null
  708        * @param offset the starting offset >= 0
  709        * @param length the length >= 0
  710        * @return the set of instances
  711        */
  712       protected Vector getPositionsInRange(Vector v, int offset, int length) {
  713           int endOffset = offset + length;
  714           int startIndex;
  715           int endIndex;
  716           int g0 = getGapStart();
  717           int g1 = getGapEnd();
  718   
  719           // Find the index of the marks.
  720           if (offset < g0) {
  721               if (offset == 0) {
  722                   // findMarkAdjustIndex start at 1!
  723                   startIndex = 0;
  724               }
  725               else {
  726                   startIndex = findMarkAdjustIndex(offset);
  727               }
  728               if (endOffset >= g0) {
  729                   endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
  730               }
  731               else {
  732                   endIndex = findMarkAdjustIndex(endOffset + 1);
  733               }
  734           }
  735           else {
  736               startIndex = findMarkAdjustIndex(offset + (g1 - g0));
  737               endIndex = findMarkAdjustIndex(endOffset + (g1 - g0) + 1);
  738           }
  739   
  740           Vector placeIn = (v == null) ? new Vector(Math.max(1, endIndex -
  741                                                              startIndex)) : v;
  742   
  743           for (int counter = startIndex; counter < endIndex; counter++) {
  744               placeIn.addElement(new UndoPosRef(marks.elementAt(counter)));
  745           }
  746           return placeIn;
  747       }
  748   
  749       /**
  750        * Resets the location for all the UndoPosRef instances
  751        * in <code>positions</code>.
  752        * <p>
  753        * This is meant for internal usage, and is generally not of interest
  754        * to subclasses.
  755        *
  756        * @param positions the UndoPosRef instances to reset
  757        */
  758       protected void updateUndoPositions(Vector positions, int offset,
  759                                          int length) {
  760           // Find the indexs of the end points.
  761           int endOffset = offset + length;
  762           int g1 = getGapEnd();
  763           int startIndex;
  764           int endIndex = findMarkAdjustIndex(g1 + 1);
  765   
  766           if (offset != 0) {
  767               startIndex = findMarkAdjustIndex(g1);
  768           }
  769           else {
  770               startIndex = 0;
  771           }
  772   
  773           // Reset the location of the refenences.
  774           for(int counter = positions.size() - 1; counter >= 0; counter--) {
  775               UndoPosRef ref = (UndoPosRef)positions.elementAt(counter);
  776               ref.resetLocation(endOffset, g1);
  777           }
  778           // We have to resort the marks in the range startIndex to endIndex.
  779           // We can take advantage of the fact that it will be in
  780           // increasing order, accept there will be a bunch of MarkData's with
  781           // the index g1 (or 0 if offset == 0) interspersed throughout.
  782           if (startIndex < endIndex) {
  783               Object[] sorted = new Object[endIndex - startIndex];
  784               int addIndex = 0;
  785               int counter;
  786               if (offset == 0) {
  787                   // If the offset is 0, the positions won't have incremented,
  788                   // have to do the reverse thing.
  789                   // Find the elements in startIndex whose index is 0
  790                   for (counter = startIndex; counter < endIndex; counter++) {
  791                       MarkData mark = marks.elementAt(counter);
  792                       if (mark.index == 0) {
  793                           sorted[addIndex++] = mark;
  794                       }
  795                   }
  796                   for (counter = startIndex; counter < endIndex; counter++) {
  797                       MarkData mark = marks.elementAt(counter);
  798                       if (mark.index != 0) {
  799                           sorted[addIndex++] = mark;
  800                       }
  801                   }
  802               }
  803               else {
  804                   for (counter = startIndex; counter < endIndex; counter++) {
  805                       MarkData mark = marks.elementAt(counter);
  806                       if (mark.index != g1) {
  807                           sorted[addIndex++] = mark;
  808                       }
  809                   }
  810                   for (counter = startIndex; counter < endIndex; counter++) {
  811                       MarkData mark = marks.elementAt(counter);
  812                       if (mark.index == g1) {
  813                           sorted[addIndex++] = mark;
  814                       }
  815                   }
  816               }
  817               // And replace
  818               marks.replaceRange(startIndex, endIndex, sorted);
  819           }
  820       }
  821   
  822       /**
  823        * Used to hold a reference to a Mark that is being reset as the
  824        * result of removing from the content.
  825        */
  826       final class UndoPosRef {
  827           UndoPosRef(MarkData rec) {
  828               this.rec = rec;
  829               this.undoLocation = rec.getOffset();
  830           }
  831   
  832           /**
  833            * Resets the location of the Position to the offset when the
  834            * receiver was instantiated.
  835            *
  836            * @param endOffset end location of inserted string.
  837            * @param g1 resulting end of gap.
  838            */
  839           protected void resetLocation(int endOffset, int g1) {
  840               if (undoLocation != endOffset) {
  841                   this.rec.index = undoLocation;
  842               }
  843               else {
  844                   this.rec.index = g1;
  845               }
  846           }
  847   
  848           /** Previous Offset of rec. */
  849           protected int undoLocation;
  850           /** Mark to reset offset. */
  851           protected MarkData rec;
  852       } // End of GapContent.UndoPosRef
  853   
  854   
  855       /**
  856        * UnoableEdit created for inserts.
  857        */
  858       class InsertUndo extends AbstractUndoableEdit {
  859           protected InsertUndo(int offset, int length) {
  860               super();
  861               this.offset = offset;
  862               this.length = length;
  863           }
  864   
  865           public void undo() throws CannotUndoException {
  866               super.undo();
  867               try {
  868                   // Get the Positions in the range being removed.
  869                   posRefs = getPositionsInRange(null, offset, length);
  870                   string = getString(offset, length);
  871                   remove(offset, length);
  872               } catch (BadLocationException bl) {
  873                 throw new CannotUndoException();
  874               }
  875           }
  876   
  877           public void redo() throws CannotRedoException {
  878               super.redo();
  879               try {
  880                   insertString(offset, string);
  881                   string = null;
  882                   // Update the Positions that were in the range removed.
  883                   if(posRefs != null) {
  884                       updateUndoPositions(posRefs, offset, length);
  885                       posRefs = null;
  886                   }
  887               } catch (BadLocationException bl) {
  888                   throw new CannotRedoException();
  889               }
  890           }
  891   
  892           /** Where string was inserted. */
  893           protected int offset;
  894           /** Length of string inserted. */
  895           protected int length;
  896           /** The string that was inserted. This will only be valid after an
  897            * undo. */
  898           protected String string;
  899           /** An array of instances of UndoPosRef for the Positions in the
  900            * range that was removed, valid after undo. */
  901           protected Vector posRefs;
  902       } // GapContent.InsertUndo
  903   
  904   
  905       /**
  906        * UndoableEdit created for removes.
  907        */
  908       class RemoveUndo extends AbstractUndoableEdit {
  909           protected RemoveUndo(int offset, String string) {
  910               super();
  911               this.offset = offset;
  912               this.string = string;
  913               this.length = string.length();
  914               posRefs = getPositionsInRange(null, offset, length);
  915           }
  916   
  917           public void undo() throws CannotUndoException {
  918               super.undo();
  919               try {
  920                   insertString(offset, string);
  921                   // Update the Positions that were in the range removed.
  922                   if(posRefs != null) {
  923                       updateUndoPositions(posRefs, offset, length);
  924                       posRefs = null;
  925                   }
  926                   string = null;
  927               } catch (BadLocationException bl) {
  928                 throw new CannotUndoException();
  929               }
  930           }
  931   
  932           public void redo() throws CannotRedoException {
  933               super.redo();
  934               try {
  935                   string = getString(offset, length);
  936                   // Get the Positions in the range being removed.
  937                   posRefs = getPositionsInRange(null, offset, length);
  938                   remove(offset, length);
  939               } catch (BadLocationException bl) {
  940                 throw new CannotRedoException();
  941               }
  942           }
  943   
  944           /** Where the string was removed from. */
  945           protected int offset;
  946           /** Length of string removed. */
  947           protected int length;
  948           /** The string that was removed. This is valid when redo is valid. */
  949           protected String string;
  950           /** An array of instances of UndoPosRef for the Positions in the
  951            * range that was removed, valid before undo. */
  952           protected Vector posRefs;
  953       } // GapContent.RemoveUndo
  954   }

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