001: package net.sf.saxon.om;
002:
003: import net.sf.saxon.tinytree.CharSlice;
004:
005: import java.io.Writer;
006: import java.io.Serializable;
007:
008: /**
009: * A simple implementation of a class similar to StringBuffer. Unlike
010: * StringBuffer it is not synchronized. It also offers the capability
011: * to remove unused space. (This class could possibly be replaced by
012: * StringBuilder in JDK 1.5, but using our own class gives more control.)
013: */
014:
015: public final class FastStringBuffer implements CharSequence,
016: Serializable {
017:
018: private char[] array;
019: private int used = 0;
020:
021: public FastStringBuffer(int initialSize) {
022: array = new char[initialSize];
023: }
024:
025: /**
026: * Append the contents of a String to the buffer
027: * @param s the String to be appended
028: */
029:
030: public void append(String s) {
031: int len = s.length();
032: ensureCapacity(len);
033: s.getChars(0, len, array, used);
034: used += len;
035: }
036:
037: /**
038: * Append the contents of a CharSlice to the buffer
039: * @param s the String to be appended
040: */
041:
042: public void append(CharSlice s) {
043: int len = s.length();
044: ensureCapacity(len);
045: s.copyTo(array, used);
046: used += len;
047: }
048:
049: /**
050: * Append the contents of a FastStringBuffer to the buffer
051: * @param s the FastStringBuffer to be appended
052: */
053:
054: public void append(FastStringBuffer s) {
055: int len = s.length();
056: ensureCapacity(len);
057: s.getChars(0, len, array, used);
058: used += len;
059: }
060:
061: /**
062: * Append the contents of a StringBuffer to the buffer
063: * @param s the StringBuffer to be appended
064: */
065:
066: public void append(StringBuffer s) {
067: int len = s.length();
068: ensureCapacity(len);
069: s.getChars(0, len, array, used);
070: used += len;
071: }
072:
073: /**
074: * Append the contents of a general CharSequence to the buffer
075: * @param s the CharSequence to be appended
076: */
077:
078: public void append(CharSequence s) {
079: // Although we provide variants of this method for different subtypes, Java decides which to use based
080: // on the static type of the operand. We want to use the right method based on the dynamic type, to avoid
081: // creating objects and copying strings unnecessarily. So we do a dynamic dispatch.
082: final int len = s.length();
083: ensureCapacity(len);
084: if (s instanceof CharSlice) {
085: ((CharSlice) s).copyTo(array, used);
086: } else if (s instanceof String) {
087: ((String) s).getChars(0, len, array, used);
088: } else if (s instanceof FastStringBuffer) {
089: ((FastStringBuffer) s).getChars(0, len, array, used);
090: } else {
091: s.toString().getChars(0, len, array, used);
092: }
093: used += len;
094: }
095:
096: /**
097: * Append the contents of a character array to the buffer
098: * @param srcArray the array whose contents are to be added
099: * @param start the offset of the first character in the array to be copied
100: * @param length the number of characters to be copied
101: */
102:
103: public void append(char[] srcArray, int start, int length) {
104: ensureCapacity(length);
105: System.arraycopy(srcArray, start, array, used, length);
106: used += length;
107: }
108:
109: /**
110: * Append a character to the buffer
111: * @param ch the character to be added
112: */
113:
114: public void append(char ch) {
115: ensureCapacity(1);
116: array[used++] = ch;
117: }
118:
119: /**
120: * Returns the length of this character sequence. The length is the number
121: * of 16-bit <code>char</code>s in the sequence.</p>
122: *
123: * @return the number of <code>char</code>s in this sequence
124: */
125: public int length() {
126: return used;
127: }
128:
129: /**
130: * Returns the <code>char</code> value at the specified index. An index ranges from zero
131: * to <tt>length() - 1</tt>. The first <code>char</code> value of the sequence is at
132: * index zero, the next at index one, and so on, as for array
133: * indexing. </p>
134: * <p/>
135: * <p>If the <code>char</code> value specified by the index is a
136: * <a href="Character.html#unicode">surrogate</a>, the surrogate
137: * value is returned.
138: *
139: * @param index the index of the <code>char</code> value to be returned
140: * @return the specified <code>char</code> value
141: * @throws IndexOutOfBoundsException if the <tt>index</tt> argument is negative or not less than
142: * <tt>length()</tt>
143: */
144: public char charAt(int index) {
145: if (index >= used) {
146: throw new IndexOutOfBoundsException("" + index);
147: }
148: return array[index];
149: }
150:
151: /**
152: * Returns a new <code>CharSequence</code> that is a subsequence of this sequence.
153: * The subsequence starts with the <code>char</code> value at the specified index and
154: * ends with the <code>char</code> value at index <tt>end - 1</tt>. The length
155: * (in <code>char</code>s) of the
156: * returned sequence is <tt>end - start</tt>, so if <tt>start == end</tt>
157: * then an empty sequence is returned. </p>
158: *
159: * @param start the start index, inclusive
160: * @param end the end index, exclusive
161: * @return the specified subsequence
162: * @throws IndexOutOfBoundsException if <tt>start</tt> or <tt>end</tt> are negative,
163: * if <tt>end</tt> is greater than <tt>length()</tt>,
164: * or if <tt>start</tt> is greater than <tt>end</tt>
165: */
166: public CharSequence subSequence(int start, int end) {
167: return new CharSlice(array, start, end - start);
168: }
169:
170: /**
171: * Copies characters from this FastStringBuffer into the destination character
172: * array.
173: * <p>
174: * The first character to be copied is at index <code>srcBegin</code>;
175: * the last character to be copied is at index <code>srcEnd-1</code>
176: * (thus the total number of characters to be copied is
177: * <code>srcEnd-srcBegin</code>). The characters are copied into the
178: * subarray of <code>dst</code> starting at index <code>dstBegin</code>
179: * and ending at index:
180: * <p><blockquote><pre>
181: * dstbegin + (srcEnd-srcBegin) - 1
182: * </pre></blockquote>
183: *
184: * @param srcBegin index of the first character in the string
185: * to copy.
186: * @param srcEnd index after the last character in the string
187: * to copy.
188: * @param dst the destination array.
189: * @param dstBegin the start offset in the destination array.
190: * @exception IndexOutOfBoundsException If any of the following
191: * is true:
192: * <ul><li><code>srcBegin</code> is negative.
193: * <li><code>srcBegin</code> is greater than <code>srcEnd</code>
194: * <li><code>srcEnd</code> is greater than the length of this
195: * string
196: * <li><code>dstBegin</code> is negative
197: * <li><code>dstBegin+(srcEnd-srcBegin)</code> is larger than
198: * <code>dst.length</code></ul>
199: */
200: public void getChars(int srcBegin, int srcEnd, char dst[],
201: int dstBegin) {
202: if (srcBegin < 0) {
203: throw new StringIndexOutOfBoundsException(srcBegin);
204: }
205: if (srcEnd > used) {
206: throw new StringIndexOutOfBoundsException(srcEnd);
207: }
208: if (srcBegin > srcEnd) {
209: throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
210: }
211: System.arraycopy(array, srcBegin, dst, dstBegin, srcEnd
212: - srcBegin);
213: }
214:
215: /**
216: * Convert contents of the FastStringBuffer to a string
217: */
218:
219: public String toString() {
220: condense();
221: return new String(array, 0, used);
222: }
223:
224: /**
225: * Set the character at a particular offset
226: * @param index the index of the character to be set
227: * @param ch the new character to overwrite the existing character at that location
228: * @throws IndexOutOfBoundsException if int<0 or int>=length()
229: */
230:
231: public void setCharAt(int index, char ch) {
232: if (index < 0 || index > used) {
233: throw new IndexOutOfBoundsException("" + index);
234: }
235: array[index] = ch;
236: }
237:
238: /**
239: * Set the length. If this exceeds the current length, this method is a no-op.
240: * If this is less than the current length, characters beyond the specified point
241: * are deleted.
242: * @param length the new length
243: */
244:
245: public void setLength(int length) {
246: if (length < 0 || length > used) {
247: return;
248: }
249: used = length;
250: }
251:
252: /**
253: * Expand the character array if necessary to ensure capacity for appended data
254: */
255:
256: public void ensureCapacity(int extra) {
257: if (used + extra > array.length) {
258: int newlen = array.length * 2;
259: if (newlen < used + extra) {
260: newlen = used + extra * 2;
261: }
262: char[] array2 = new char[newlen];
263: System.arraycopy(array, 0, array2, 0, used);
264: array = array2;
265: }
266: }
267:
268: /**
269: * Remove surplus space from the array. This doesn't reduce the array to the minimum
270: * possible size; it only reclaims space if it seems worth doing. Specifically, it
271: * contracts the array if the amount of wasted space is more than 256 characters, or
272: * more than half the allocated size.
273: */
274:
275: public CharSequence condense() {
276: if (array.length - used > 256 || array.length > used * 2) {
277: char[] array2 = new char[used];
278: System.arraycopy(array, 0, array2, 0, used);
279: array = array2;
280: }
281: return this ;
282: }
283:
284: /**
285: * Write the value to a writer
286: */
287:
288: public void write(Writer writer) throws java.io.IOException {
289: writer.write(array, 0, used);
290: }
291: }
292:
293: //
294: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
295: // you may not use this file except in compliance with the License. You may obtain a copy of the
296: // License at http://www.mozilla.org/MPL/
297: //
298: // Software distributed under the License is distributed on an "AS IS" basis,
299: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
300: // See the License for the specific language governing rights and limitations under the License.
301: //
302: // The Original Code is: all this file.
303: //
304: // The Initial Developer of the Original Code is Michael H. Kay
305: //
306: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
307: //
308: // Contributor(s): none
309: //
|