001: /* ====================================================================
002: * The Jcorporate Apache Style Software License, Version 1.2 05-07-2002
003: *
004: * Copyright (c) 1995-2003 Jcorporate Ltd. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution,
019: * if any, must include the following acknowledgment:
020: * "This product includes software developed by Jcorporate Ltd.
021: * (http://www.jcorporate.com/)."
022: * Alternately, this acknowledgment may appear in the software itself,
023: * if and wherever such third-party acknowledgments normally appear.
024: *
025: * 4. "Jcorporate" and product names such as "Expresso" must
026: * not be used to endorse or promote products derived from this
027: * software without prior written permission. For written permission,
028: * please contact info@jcorporate.com.
029: *
030: * 5. Products derived from this software may not be called "Expresso",
031: * or other Jcorporate product names; nor may "Expresso" or other
032: * Jcorporate product names appear in their name, without prior
033: * written permission of Jcorporate Ltd.
034: *
035: * 6. No product derived from this software may compete in the same
036: * market space, i.e. framework, without prior written permission
037: * of Jcorporate Ltd. For written permission, please contact
038: * partners@jcorporate.com.
039: *
040: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
041: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
042: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
043: * DISCLAIMED. IN NO EVENT SHALL JCORPORATE LTD OR ITS CONTRIBUTORS
044: * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
045: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
046: * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
047: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
048: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
049: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
050: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
051: * SUCH DAMAGE.
052: * ====================================================================
053: *
054: * This software consists of voluntary contributions made by many
055: * individuals on behalf of the Jcorporate Ltd. Contributions back
056: * to the project(s) are encouraged when you make modifications.
057: * Please send them to support@jcorporate.com. For more information
058: * on Jcorporate Ltd. and its products, please see
059: * <http://www.jcorporate.com/>.
060: *
061: * Portions of this software are based upon other open source
062: * products and are subject to their respective licenses.
063: */
064:
065: package com.jcorporate.expresso.kernel.util;
066:
067: import org.apache.commons.pool.BasePoolableObjectFactory;
068: import org.apache.commons.pool.ObjectPool;
069: import org.apache.commons.pool.impl.StackObjectPool;
070:
071: /**
072: * <p>A fast string buffer implements a mutable sequence of characters.
073: * </p>
074: * <p>Fast string buffers are unsafe for use by multiple threads.
075: * </p>
076: * <p>Every string buffer has a capacity. As long as the length of the
077: * character sequence contained in the string buffer does not exceed
078: * the capacity, it is not necessary to allocate a new internal
079: * buffer array. If the internal buffer overflows, it is
080: * automatically made larger.</p>
081: * <p>There are several performance improvements that make it worthwhile to
082: * use this class over the standard JDK <code>StringBuffer</code>
083: * <ul>
084: * <li>Method calls are unsynchronized. Calls to synchronized methods are
085: * automatically at least four times slower than unsynchronized methods</li>
086: * <li>Pooling. By using the pooling mechanism, you basically remove all
087: * string allocation overhead and the fast string buffer is most likely
088: * pre-allocated to a size you'll use</li>
089: * <li>Non-zeroing out. <code>StringBuffer</code> zero's out it's character array,
090: * even though it's not necessary. FastStringBuffer doesn't perform any memory
091: * zeroing<li>
092: * <li>Parameter Checks removed. FastStringBuffer doesn't perform any out of bounds
093: * checks against incoming strings and parameters. This automatically saves overhead
094: * at the expense of you potentially getting less descriptive error messages</li>
095: * <li>Reuse. By calling the <code>clear()</code> method you can reuse a FastStringBuffer
096: * thus saving yourself a lot of memory allocations</li>
097: * </ul>
098: *
099: * @see java.io.ByteArrayOutputStream
100: * @see java.lang.StringBuffer
101: * @since Expresso 4.0, Pooling Mechanism since Expreso 5.0
102: */
103: public final class FastStringBuffer implements java.io.Serializable {
104:
105: /**
106: * The value is used for character storage.
107: */
108: private char[] value;
109:
110: /**
111: * The count is the number of characters in the buffer.
112: */
113: private int count;
114:
115: /**
116: * A flag indicating whether the buffer is shared
117: */
118: private boolean shared;
119:
120: static FastStringBuffer pointerInstance = null;
121:
122: /**
123: * Pointer to the object factory. [Which is a inner class]
124: */
125: private static FastStringBufferObjectFactory factory = null;
126:
127: /**
128: * The Apache Commons Object Pool
129: */
130: private static ObjectPool thePool = null;
131:
132: /** use serialVersionUID from JDK 1.0.2 for interoperability */
133: // static final long serialVersionUID = 3388685877147921107L;
134: /**
135: * Constructs a string buffer with no characters in it and an
136: * initial capacity of 16 characters.
137: */
138: public FastStringBuffer() {
139: this (16);
140: }
141:
142: /**
143: * Constructs a string buffer with no characters in it and an
144: * initial capacity specified by the <code>length</code> argument.
145: *
146: * @param length the initial capacity.
147: * @throws NegativeArraySizeException if the <code>length</code>
148: * argument is less than <code>0</code>.
149: */
150: public FastStringBuffer(int length) {
151: synchronized (FastStringBuffer.class) {
152: if (factory == null) {
153: factory = new FastStringBuffer.FastStringBufferObjectFactory();
154: }
155: }
156: value = new char[length];
157: shared = false;
158: }
159:
160: /**
161: * Constructs a string buffer so that it represents the same
162: * sequence of characters as the string argument. The initial
163: * capacity of the string buffer is <code>16</code> plus the length
164: * of the string argument.
165: *
166: * @param str the initial contents of the buffer.
167: */
168: public FastStringBuffer(String str) {
169: this (str.length() + 16);
170: append(str);
171:
172: synchronized (FastStringBuffer.class) {
173: if (factory == null) {
174: factory = new FastStringBuffer.FastStringBufferObjectFactory();
175: }
176: }
177:
178: }
179:
180: /**
181: * Returns the length (character count) of this string buffer.
182: *
183: * @return the number of characters in this string buffer.
184: */
185: public int length() {
186: return count;
187: }
188:
189: /**
190: * Returns the current capacity of the String buffer. The capacity
191: * is the amount of storage available for newly inserted
192: * characters; beyond which an allocation will occur.
193: *
194: * @return the current capacity of this string buffer.
195: */
196: public int capacity() {
197: return value.length;
198: }
199:
200: /**
201: * Clears the buffer and prepares it for reuse.
202: */
203: public void clear() {
204: copyWhenShared();
205: count = 0;
206: }
207:
208: /**
209: * Copies the buffer value if it is shared.
210: */
211: private final void copyWhenShared() {
212: if (shared) {
213: char[] newValue = new char[value.length];
214: System.arraycopy(value, 0, newValue, 0, count);
215: value = newValue;
216: shared = false;
217: }
218: }
219:
220: /**
221: * Ensures that the capacity of the buffer is at least equal to the
222: * specified minimum.
223: * If the current capacity of this string buffer is less than the
224: * argument, then a new internal buffer is allocated with greater
225: * capacity. The new capacity is the larger of:
226: * <ul>
227: * <li>The <code>minimumCapacity</code> argument.
228: * <li>Twice the old capacity, plus <code>2</code>.
229: * </ul>
230: * If the <code>minimumCapacity</code> argument is nonpositive, this
231: * method takes no action and simply returns.
232: *
233: * @param minimumCapacity the minimum desired capacity.
234: */
235: public void ensureCapacity(int minimumCapacity) {
236: int maxCapacity = value.length;
237:
238: if (minimumCapacity > maxCapacity) {
239: int newCapacity = maxCapacity + maxCapacity + 2;
240:
241: if (minimumCapacity > newCapacity) {
242: newCapacity = minimumCapacity;
243: }
244:
245: char[] newValue = new char[newCapacity];
246: System.arraycopy(value, 0, newValue, 0, count);
247: value = newValue;
248: shared = false;
249: }
250: }
251:
252: /**
253: * Sets the length of this String buffer.
254: * If the <code>newLength</code> argument is less than the current
255: * length of the string buffer, the string buffer is truncated to
256: * contain exactly the number of characters given by the
257: * <code>newLength</code> argument.
258: * <p/>
259: * If the <code>newLength</code> argument is greater than or equal
260: * to the current length, sufficient null characters
261: * (<code>'\u0000'</code>) are appended to the string buffer so that
262: * length becomes the <code>newLength</code> argument.
263: * <p/>
264: * The <code>newLength</code> argument must be greater than or equal
265: * to <code>0</code>.
266: *
267: * @param newLength the new length of the buffer.
268: * @see java.lang.StringBuffer#length()
269: */
270: public void setLength(int newLength) {
271: ensureCapacity(newLength);
272:
273: if (count < newLength) {
274: copyWhenShared();
275:
276: // for (; count < newLength; count++) {
277: // value[count] = '\0';
278: // }
279: }
280:
281: count = newLength;
282: }
283:
284: /**
285: * Returns the character at a specific index in this string buffer.
286: * <p/>
287: * The first character of a string buffer is at index
288: * <code>0</code>, the next at index <code>1</code>, and so on, for
289: * array indexing.
290: * <p/>
291: * The index argument must be greater than or equal to
292: * <code>0</code>, and less than the length of this string buffer.
293: *
294: * @param index the index of the desired character.
295: * @return the character at the specified index of this string buffer.
296: * @see java.lang.StringBuffer#length()
297: */
298: public char charAt(int index) {
299: return value[index];
300: }
301:
302: /**
303: * Characters are copied from this string buffer into the
304: * destination character array <code>dst</code>. The first character to
305: * be copied is at index <code>srcBegin</code>; the last character to
306: * be copied is at index <code>srcEnd-1.</code> The total number of
307: * characters to be copied is <code>srcEnd-srcBegin</code>. The
308: * characters are copied into the subarray of <code>dst</code> starting
309: * at index <code>dstBegin</code> and ending at index:
310: * <p><blockquote><pre>
311: * dstbegin + (srcEnd-srcBegin) - 1
312: * </pre></blockquote>
313: *
314: * @param srcBegin start copying at this offset in the string buffer.
315: * @param srcEnd stop copying at this offset in the string buffer.
316: * @param dst the array to copy the data into.
317: * @param dstBegin offset into <code>dst</code>.
318: * @throws StringIndexOutOfBoundsException
319: * if there is an invalid
320: * index into the buffer.
321: */
322: public void getChars(int srcBegin, int srcEnd, char[] dst,
323: int dstBegin) {
324: System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd
325: - srcBegin);
326: }
327:
328: /**
329: * The character at the specified index of this string buffer is set
330: * to <code>ch</code>.
331: * <p/>
332: * The offset argument must be greater than or equal to
333: * <code>0</code>, and less than the length of this string buffer.
334: *
335: * @param index the index of the character to modify.
336: * @param ch the new character.
337: * @throws StringIndexOutOfBoundsException
338: * if the index is invalid.
339: * @see java.lang.StringBuffer#length()
340: */
341: public void setCharAt(int index, char ch) {
342: copyWhenShared();
343: value[index] = ch;
344: }
345:
346: /**
347: * Appends the string representation of the <code>Object</code>
348: * argument to this string buffer.
349: * <p/>
350: * The argument is converted to a string as if by the method
351: * <code>String.valueOf</code>, and the characters of that
352: * string are then appended to this string buffer.
353: *
354: * @param obj an <code>Object</code>.
355: * @return this string buffer.
356: * @see java.lang.String#valueOf(java.lang.Object)
357: * @see java.lang.StringBuffer#append(java.lang.String)
358: */
359: public FastStringBuffer append(Object obj) {
360: return append(String.valueOf(obj));
361: }
362:
363: /**
364: * Appends the string to this string buffer.
365: * <p/>
366: * The characters of the <code>String</code> argument are appended, in
367: * order, to the contents of this string buffer, increasing the
368: * length of this string buffer by the length of the argument.
369: *
370: * @param str a string.
371: * @return this string buffer.
372: */
373: public FastStringBuffer append(String str) {
374: if (str == null) {
375: str = String.valueOf(str);
376: }
377:
378: int len = str.length();
379: ensureCapacity(count + len);
380: copyWhenShared();
381: str.getChars(0, len, value, count);
382: count += len;
383:
384: return this ;
385: }
386:
387: /**
388: * Appends the string representation of the <code>char</code> array
389: * argument to this string buffer.
390: * <p/>
391: * The characters of the array argument are appended, in order, to
392: * the contents of this string buffer. The length of this string
393: * buffer increases by the length of the argument.
394: *
395: * @param str the characters to be appended.
396: * @return this string buffer.
397: */
398: public FastStringBuffer append(char[] str) {
399: int len = str.length;
400: ensureCapacity(count + len);
401: copyWhenShared();
402: System.arraycopy(str, 0, value, count, len);
403: count += len;
404:
405: return this ;
406: }
407:
408: /**
409: * Appends the string representation of a subarray of the
410: * <code>char</code> array argument to this string buffer.
411: * <p/>
412: * Characters of the character array <code>str</code>, starting at
413: * index <code>offset</code>, are appended, in order, to the contents
414: * of this string buffer. The length of this string buffer increases
415: * by the value of <code>len</code>.
416: *
417: * @param str the characters to be appended.
418: * @param offset the index of the first character to append.
419: * @param len the number of characters to append.
420: * @return this string buffer.
421: */
422: public FastStringBuffer append(char[] str, int offset, int len) {
423: ensureCapacity(count + len);
424: copyWhenShared();
425: System.arraycopy(str, offset, value, count, len);
426: count += len;
427:
428: return this ;
429: }
430:
431: /**
432: * Appends one FastStringBuffer to another so they can be merged with no
433: * unnecessary allocations
434: *
435: * @param str The string buffer to append to.
436: * @return this string buffer
437: */
438: public FastStringBuffer append(FastStringBuffer str) {
439: int len = str.length();
440: ensureCapacity(count + str.count);
441: copyWhenShared();
442: System.arraycopy(str.getValue(), 0, value, count, len);
443: count += len;
444:
445: return this ;
446: }
447:
448: /**
449: * Appends the string representation of the <code>boolean</code>
450: * argument to the string buffer.
451: * <p/>
452: * The argument is converted to a string as if by the method
453: * <code>String.valueOf</code>, and the characters of that
454: * string are then appended to this string buffer.
455: *
456: * @param b a <code>boolean</code>.
457: * @return this string buffer.
458: * @see java.lang.String#valueOf(boolean)
459: * @see java.lang.StringBuffer#append(java.lang.String)
460: */
461: public FastStringBuffer append(boolean b) {
462: return append(String.valueOf(b));
463: }
464:
465: /**
466: * Appends the string representation of the <code>char</code>
467: * argument to this string buffer.
468: * <p/>
469: * The argument is appended to the contents of this string buffer.
470: * The length of this string buffer increases by <code>1</code>.
471: *
472: * @param c a <code>char</code>.
473: * @return this string buffer.
474: */
475: public FastStringBuffer append(char c) {
476: ensureCapacity(count + 1);
477: copyWhenShared();
478: value[count++] = c;
479:
480: return this ;
481: }
482:
483: /**
484: * Appends the string representation of the <code>int</code>
485: * argument to this string buffer.
486: * <p/>
487: * The argument is converted to a string as if by the method
488: * <code>String.valueOf</code>, and the characters of that
489: * string are then appended to this string buffer.
490: *
491: * @param i an <code>int</code>.
492: * @return this string buffer.
493: * @see java.lang.String#valueOf(int)
494: * @see java.lang.StringBuffer#append(java.lang.String)
495: */
496: public FastStringBuffer append(int i) {
497: return append(String.valueOf(i));
498: }
499:
500: /**
501: * Appends the string representation of the <code>long</code>
502: * argument to this string buffer.
503: * <p/>
504: * The argument is converted to a string as if by the method
505: * <code>String.valueOf</code>, and the characters of that
506: * string are then appended to this string buffer.
507: *
508: * @param l a <code>long</code>.
509: * @return this string buffer.
510: * @see java.lang.String#valueOf(long)
511: * @see java.lang.StringBuffer#append(java.lang.String)
512: */
513: public FastStringBuffer append(long l) {
514: return append(String.valueOf(l));
515: }
516:
517: /**
518: * Appends the string representation of the <code>float</code>
519: * argument to this string buffer.
520: * <p/>
521: * The argument is converted to a string as if by the method
522: * <code>String.valueOf</code>, and the characters of that
523: * string are then appended to this string buffer.
524: *
525: * @param f a <code>float</code>.
526: * @return this string buffer.
527: * @see java.lang.String#valueOf(float)
528: * @see java.lang.StringBuffer#append(java.lang.String)
529: */
530: public FastStringBuffer append(float f) {
531: return append(String.valueOf(f));
532: }
533:
534: /**
535: * Appends the string representation of the <code>double</code>
536: * argument to this string buffer.
537: * <p/>
538: * The argument is converted to a string as if by the method
539: * <code>String.valueOf</code>, and the characters of that
540: * string are then appended to this string buffer.
541: *
542: * @param d a <code>double</code>.
543: * @return this string buffer.
544: * @see java.lang.String#valueOf(double)
545: * @see java.lang.StringBuffer#append(java.lang.String)
546: */
547: public FastStringBuffer append(double d) {
548: return append(String.valueOf(d));
549: }
550:
551: /**
552: * The character sequence contained in this string buffer is
553: * replaced by the reverse of the sequence.
554: *
555: * @return this string buffer.
556: */
557: public FastStringBuffer reverse() {
558: copyWhenShared();
559:
560: int n = count - 1;
561:
562: for (int j = (n - 1) >> 1; j >= 0; --j) {
563: char temp = value[j];
564: value[j] = value[n - j];
565: value[n - j] = temp;
566: }
567:
568: return this ;
569: }
570:
571: /**
572: * Converts to a string representing the data in this string buffer.
573: * A new <code>String</code> object is allocated and initialized to
574: * contain the character sequence currently represented by this
575: * string buffer. This <code>String</code> is then returned. Subsequent
576: * changes to the string buffer do not affect the contents of the
577: * <code>String</code>.
578: *
579: * @return a string representation of the string buffer.
580: */
581: public String toString() {
582: return new String(value, 0, count);
583: }
584:
585: //
586: // The following two methods are needed by String to efficiently
587: // convert a StringBuffer into a String. They are not public.
588: // They shouldn't be called by anyone but String.
589: final void setShared() {
590: shared = true;
591: }
592:
593: final char[] getValue() {
594: return value;
595: }
596:
597: /**
598: * Retrieve an instance of FastString buffer from the object pool. The object
599: * is often preallocated to a 1024 byte size from a pool of objects. This
600: * can drastically reduce small memory allocations and increate performance.
601: *
602: * @return an instantiaged FastStringBuffer instance.
603: */
604: public synchronized static FastStringBuffer getInstance() {
605: synchronized (FastStringBuffer.class) {
606: if (FastStringBuffer.pointerInstance == null) {
607: pointerInstance = new FastStringBuffer(1);
608: thePool = new StackObjectPool(factory);
609: }
610:
611: try {
612: return (FastStringBuffer) thePool.borrowObject();
613:
614: } catch (Exception ex) {
615: System.err.println(ex.getMessage());
616: ex.printStackTrace();
617: return null;
618: }
619: }
620: }
621:
622: /**
623: * Sends the FastStringBuffer back to the object pool. Use this if you have
624: * obtained the FastStringBuffer instance through FastStringBuffer.getInstance()
625: */
626: public void release() {
627: synchronized (FastStringBuffer.class) {
628: try {
629: thePool.returnObject(this );
630: } catch (Exception ex) {
631: }
632: }
633: }
634:
635: /**
636: * An implementation of a PoolableObject factory to work with FastStringBuffers.
637: * Preallocates 1024-char buffers and downsizes them if over than when passivated.
638: *
639: * @author Michael Rimov
640: */
641: class FastStringBufferObjectFactory extends
642: BasePoolableObjectFactory {
643: // for makeObject we'll simply return a new buffer
644: public Object makeObject() {
645: return new FastStringBuffer(1024);
646: }
647:
648: //we'll clear it out and reset it's size to 1024 if it grew
649: //bigger than one k.
650: public void passivateObject(Object obj) {
651: FastStringBuffer buf = (FastStringBuffer) obj;
652: if (buf.value.length > 1024) {
653: buf.value = new char[1024];
654: buf.clear();
655: } else {
656: buf.clear();
657: }
658: }
659:
660: // for all other methods, the no-op
661: // implementation in BasePoolableObjectFactory
662: // will suffice
663:
664: }
665: }
|