GNU Classpath (0.17) | ||
Frames | No Frames |
1: /* GapContent.java -- 2: Copyright (C) 2002, 2004, 2005 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: 43: import javax.swing.undo.UndoableEdit; 44: 45: /** 46: * This implementation of {@link AbstractDocument.Content} uses a gapped 47: * buffer. This takes advantage of the fact that text area content is 48: * mostly inserted sequentially. The buffer is a char array that maintains 49: * a gap at the current insertion point. If characters a inserted at 50: * gap boundaries, the cost is minimal (simple array access). The array only 51: * has to be shifted around when the insertion point moves (then the gap also 52: * moves and one array copy is necessary) or when the gap is filled up and 53: * the buffer has to be enlarged. 54: * 55: * TODO: Implement UndoableEdit support stuff 56: */ 57: public class GapContent 58: implements AbstractDocument.Content, Serializable 59: { 60: private static final long serialVersionUID = 8374645204155842629L; 61: 62: /** 63: * This is the default buffer size and the amount of bytes that 64: * a buffer is extended if it is full. 65: */ 66: static final int DEFAULT_BUFSIZE = 64; 67: 68: /** 69: * The text buffer. 70: */ 71: char[] buffer; 72: 73: /** 74: * The index of the first character of the gap. 75: */ 76: int gapStart; 77: 78: /** 79: * The index of the character after the last character of the gap. 80: */ 81: int gapEnd; 82: 83: /** 84: * Creates a new GapContent object. 85: */ 86: public GapContent() 87: { 88: this(DEFAULT_BUFSIZE); 89: } 90: 91: /** 92: * Creates a new GapContent object with a specified initial size. 93: * 94: * @param size the initial size of the buffer 95: */ 96: public GapContent(int size) 97: { 98: buffer = (char[]) allocateArray(size); 99: gapStart = 0; 100: gapEnd = size - 1; 101: buffer[size - 1] = '\n'; 102: } 103: 104: /** 105: * Allocates an array of the specified length that can then be used as 106: * buffer. 107: * 108: * @param size the size of the array to be allocated 109: * 110: * @return the allocated array 111: */ 112: protected Object allocateArray(int size) 113: { 114: return new char[size]; 115: } 116: 117: /** 118: * Returns the length of the allocated buffer array. 119: * 120: * @return the length of the allocated buffer array 121: */ 122: protected int getArrayLength() 123: { 124: return buffer.length; 125: } 126: 127: /** 128: * Returns the length of the content. 129: * 130: * @return the length of the content 131: */ 132: public int length() 133: { 134: return buffer.length - (gapEnd - gapStart); 135: } 136: 137: /** 138: * Inserts a string at the specified position. 139: * 140: * @param where the position where the string is inserted 141: * @param str the string that is to be inserted 142: * 143: * @return an UndoableEdit object (currently not supported, so 144: * <code>null</code> is returned) 145: * 146: * @throws BadLocationException if <code>where</code> is not a valid location 147: * in the buffer 148: */ 149: public UndoableEdit insertString(int where, String str) 150: throws BadLocationException 151: { 152: // check arguments 153: int length = length(); 154: int strLen = str.length(); 155: 156: if (where >= length) 157: throw new BadLocationException("the where argument cannot be greater" 158: + " than the content length", where); 159: 160: // check if the gap is big enough to hold the string 161: if ((gapEnd - gapStart) < strLen) 162: // make room for this string and some more 163: shiftEnd(strLen + DEFAULT_BUFSIZE); 164: 165: // are we at the gap boundary? 166: if (where != gapStart) 167: shiftGap(where); 168: 169: // now we can simple copy the string into the gap and adjust the 170: // gap boundaries 171: System.arraycopy(str.toCharArray(), 0, buffer, gapStart, strLen); 172: gapStart += strLen; 173: return null; 174: } 175: 176: /** 177: * Removes a piece of content at th specified position. 178: * 179: * @param where the position where the content is to be removed 180: * @param nitems number of characters to be removed 181: * 182: * @return an UndoableEdit object (currently not supported, so 183: * <code>null</code> is returned) 184: * 185: * @throws BadLocationException if <code>where</code> is not a valid location 186: * in the buffer 187: */ 188: public UndoableEdit remove(int where, int nitems) 189: throws BadLocationException 190: { 191: // check arguments 192: int length = length(); 193: 194: if (where >= length) 195: throw new BadLocationException("the where argument cannot be greater" 196: + " than the content length", where); 197: if ((where + nitems) > length) 198: throw new BadLocationException("where + nitems cannot be greater" 199: + " than the content length", 200: where + nitems); 201: 202: // check if we are at the gap boundary 203: if (where != gapStart) 204: shiftGap(where); 205: 206: // now we simply have to enlarge the gap 207: gapEnd += nitems; 208: return null; 209: } 210: 211: /** 212: * Returns a piece of content as String. 213: * 214: * @param where the start location of the fragment 215: * @param len the length of the fragment 216: * 217: * @throws BadLocationException if <code>where</code> or 218: * <code>where + len</code> are no valid locations in the buffer 219: */ 220: public String getString(int where, int len) throws BadLocationException 221: { 222: Segment seg = new Segment(); 223: getChars(where, len, seg); 224: return new String(seg.array, seg.offset, seg.count); 225: } 226: 227: /** 228: * Fetches a piece of content and stores it in a {@link Segment} object. 229: * 230: * If the requested piece of text spans the gap, the content is copied 231: * into a new array. If it doesn't then it is contiguous and the 232: * actual content store is returned. 233: * 234: * @param where the start location of the fragment 235: * @param len the length of the fragment 236: * @param txt the Segment object to store the fragment in 237: * 238: * @throws BadLocationException if <code>where</code> or 239: * <code>where + len</code> are no valid locations in the buffer 240: */ 241: public void getChars(int where, int len, Segment txt) 242: throws BadLocationException 243: { 244: // check arguments 245: int length = length(); 246: if (where >= length) 247: throw new BadLocationException("the where argument cannot be greater" 248: + " than the content length", where); 249: if ((where + len) > length) 250: throw new BadLocationException("len plus where cannot be greater" 251: + " than the content length", 252: len + where); 253: 254: // check if requested segment is contiguous 255: if ((where < gapStart) && ((gapStart - where) < len)) 256: { 257: // requested segment is not contiguous -> copy the pieces together 258: char[] copy = new char[len]; 259: int lenFirst = gapStart - where; // the length of the first segment 260: System.arraycopy(buffer, where, copy, 0, lenFirst); 261: System.arraycopy(buffer, gapEnd, copy, lenFirst, len - lenFirst); 262: txt.array = copy; 263: txt.offset = 0; 264: txt.count = len; 265: } 266: else 267: { 268: // requested segment is contiguous -> we can simply return the 269: // actual content 270: txt.array = buffer; 271: if (where < gapStart) 272: txt.offset = where; 273: else 274: txt.offset = where + (gapEnd - gapStart); 275: txt.count = len; 276: } 277: } 278: 279: /** 280: * Creates and returns a mark at the specified position. 281: * 282: * @param offset the position at which to create the mark 283: * 284: * @return the create Position object for the mark 285: * 286: * @throws BadLocationException if the offset is not a valid position in 287: * the buffer 288: */ 289: public Position createPosition(final int offset) throws BadLocationException 290: { 291: return new Position() 292: { 293: int off = offset; 294: 295: public int getOffset() 296: { 297: return off; 298: } 299: }; 300: } 301: 302: /** 303: * Enlarges the gap. This allocates a new bigger buffer array, copy the 304: * segment before the gap as it is and the segment after the gap at 305: * the end of the new buffer array. This does change the gapEnd mark 306: * but not the gapStart mark. 307: * 308: * @param newSize the new size of the gap 309: */ 310: protected void shiftEnd(int newSize) 311: { 312: char[] newBuf = (char[]) allocateArray(length() + newSize); 313: System.arraycopy(buffer, 0, newBuf, 0, gapStart); 314: System.arraycopy(buffer, gapEnd, newBuf, gapStart + newSize, 315: buffer.length - gapEnd); 316: gapEnd = gapStart + newSize; 317: buffer = newBuf; 318: } 319: 320: /** 321: * Shifts the gap to the specified position. 322: * 323: * @param newGapStart the new start position of the gap 324: */ 325: protected void shiftGap(int newGapStart) 326: { 327: int newGapEnd = newGapStart + (gapEnd - gapStart); 328: 329: if (newGapStart == gapStart) 330: return; 331: else if (newGapStart < gapStart) 332: { 333: System.arraycopy(buffer, newGapStart, buffer, newGapEnd, 334: gapStart - newGapStart); 335: gapStart = newGapStart; 336: gapEnd = newGapEnd; 337: } 338: else 339: { 340: System.arraycopy(buffer, gapEnd, buffer, gapStart, 341: newGapStart - gapStart); 342: gapStart = newGapStart; 343: gapEnd = newGapEnd; 344: } 345: } 346: 347: /** 348: * Returns the allocated buffer array. 349: * 350: * @return the allocated buffer array 351: */ 352: protected Object getArray() 353: { 354: return buffer; 355: } 356: }
GNU Classpath (0.17) |