001: package com.opensymphony.module.sitemesh.html.util;
002:
003: import java.io.PrintWriter;
004:
005: /**
006: * A leaner, meaner version of StringBuffer.
007: * <p/>
008: * It provides basic functionality to handle dynamically-growing
009: * char arrays as quickly as possible. This class is not threadsafe.
010: *
011: * @author Chris Miller
012: */
013: public class CharArray {
014: int size = 0;
015: char[] buffer;
016:
017: // These properties allow us to specify a substring within the character array
018: // that we can perform comparisons against. This is here purely for performance -
019: // the comparisons are at the heart of the FastPageParser loop and any speed increase
020: // we can get at this level has a huge impact on performance.
021: int subStrStart = 0;
022: int subStrLen = 0;
023:
024: /**
025: * Constructs a CharArray that is initialized to the specified size.
026: *
027: * Do not pass in a negative value because there is no bounds checking!
028: */
029: public CharArray(int size) {
030: buffer = new char[size];
031: }
032:
033: /**
034: * Returns a String represenation of the character array.
035: */
036: public String toString() {
037: return new String(buffer, 0, size);
038: }
039:
040: /**
041: * Returns the character that is at the specified position in the array.
042: *
043: * There is no bounds checking on this method so be sure to pass in a
044: * sensible value.
045: */
046: public char charAt(int pos) {
047: return buffer[pos];
048: }
049:
050: /**
051: * Changes the size of the character array to the value specified.
052: *
053: * If the new size is less than the current size, the data in the
054: * internal array will be truncated. If the new size is <= 0,
055: * the array will be reset to empty (but, unlike StringBuffer, the
056: * internal array will NOT be shrunk). If the new size is > the
057: * current size, the array will be padded out with null characters
058: * (<tt>'\u0000'</tt>).
059: *
060: * @param newSize the new size of the character array
061: */
062: public void setLength(int newSize) {
063: if (newSize < 0) {
064: newSize = 0;
065: }
066:
067: if (newSize <= size) {
068: size = newSize;
069: } else {
070: if (newSize >= buffer.length)
071: grow(newSize);
072: // Pad the array
073: for (; size < newSize; size++)
074: buffer[size] = '\0';
075: }
076: }
077:
078: /**
079: * Returns the current length of the character array.
080: */
081: public int length() {
082: return size;
083: }
084:
085: /**
086: * Appends an existing CharArray on to this one.
087: *
088: * Passing in a <tt>null</tt> CharArray will result in a <tt>NullPointerException</tt>.
089: */
090: public CharArray append(CharArray chars) {
091: return append(chars.buffer, 0, chars.size);
092: }
093:
094: /**
095: * Appends the supplied characters to the end of the array.
096: */
097: public CharArray append(char[] chars) {
098: return append(chars, 0, chars.length);
099: }
100:
101: public CharArray append(char[] chars, int position, int length) {
102: int requiredSize = length + size;
103: if (requiredSize >= buffer.length)
104: grow(requiredSize);
105: System.arraycopy(chars, position, buffer, size, length);
106: size = requiredSize;
107: return this ;
108: }
109:
110: /**
111: * Appends a single character to the end of the character array.
112: */
113: public CharArray append(char c) {
114: if (buffer.length == size)
115: grow(0);
116: buffer[size++] = c;
117: return this ;
118: }
119:
120: /**
121: * Appends the supplied string to the end of this character array.
122: *
123: * Passing in a <tt>null</tt> string will result in a <tt>NullPointerException</tt>.
124: */
125: public CharArray append(String str) {
126: int requiredSize = str.length() + size;
127: if (requiredSize >= buffer.length)
128: grow(requiredSize);
129:
130: for (int i = 0; i < str.length(); i++)
131: buffer[size + i] = str.charAt(i);
132:
133: size = requiredSize;
134: return this ;
135: }
136:
137: /**
138: * Returns a substring from within this character array.
139: *
140: * Note that NO range checking is performed!
141: */
142: public String substring(int begin, int end) {
143: return new String(buffer, begin, end - begin);
144: }
145:
146: /**
147: * Allows an arbitrary substring of this character array to be specified.
148: * This method should be called prior to calling {@link #compareLowerSubstr(String)}
149: * to set the range of the substring comparison.
150: *
151: * @param begin the starting offset into the character array.
152: * @param end the ending offset into the character array.
153: */
154: public void setSubstr(int begin, int end) {
155: subStrStart = begin;
156: subStrLen = end - begin;
157: }
158:
159: /**
160: * Returns the substring that was specified by the {@link #setSubstr(int, int)} call.
161: */
162: public String getLowerSubstr() {
163: for (int i = subStrStart; i < subStrStart + subStrLen; i++)
164: buffer[i] |= 32;
165: return new String(buffer, subStrStart, subStrLen);
166: }
167:
168: /**
169: * This compares a substring of this character array (as specified
170: * by the {@link #setSubstr(int, int)} method call) with the supplied
171: * string. The supplied string <em>must</em> be lowercase, otherwise
172: * the comparison will fail.
173: */
174: public boolean compareLowerSubstr(String lowerStr) {
175: // Range check
176: if (lowerStr.length() != subStrLen || subStrLen <= 0)
177: return false;
178:
179: for (int i = 0; i < lowerStr.length(); i++) {
180: // | 32 converts from ASCII uppercase to ASCII lowercase
181: if ((buffer[subStrStart + i] | 32) != lowerStr.charAt(i))
182: return false;
183: }
184: return true;
185: }
186:
187: /**
188: * Returns the hashcode for a <em>lowercase</em> version of the array's substring
189: * (as set by the {@link #setSubstr(int, int)} method).
190: *
191: * This uses the same calculation as the <tt>String.hashCode()</tt> method
192: * so that it remains compatible with the hashcodes of normal strings.
193: */
194: public int substrHashCode() {
195: int hash = 0;
196: int offset = subStrStart;
197: for (int i = 0; i < subStrLen; i++) {
198: hash = 31 * hash + (buffer[offset++] | 32);
199: }
200: return hash;
201: }
202:
203: /**
204: * Compares the supplied uppercase string with the contents of
205: * the character array, starting at the offset specified.
206: *
207: * This is a specialized method to help speed up the FastPageParser
208: * slightly.
209: * <p/>
210: * The supplied string is assumed to contain only uppercase ASCII
211: * characters. The offset indicates the offset into the character
212: * array that the comparison should start from.
213: * <p/>
214: * If (and only if) the supplied string and the relevant portion of the
215: * character array are considered equal, this method will return <tt>true</tt>.
216: */
217: public boolean compareLower(String lowerStr, int offset) {
218: // Range check
219: if (offset < 0 || offset + lowerStr.length() > size)
220: return false;
221:
222: for (int i = 0; i < lowerStr.length(); i++) {
223: // | 32 converts from ASCII uppercase to ASCII lowercase
224: if ((buffer[offset + i] | 32) != lowerStr.charAt(i))
225: return false;
226: }
227: return true;
228: }
229:
230: /**
231: * Grows the internal array by either ~100% or minSize (whichever is larger),
232: * up to a maximum size of Integer.MAX_VALUE.
233: */
234: private final void grow(int minSize) {
235: int newCapacity = (buffer.length + 1) * 2;
236: if (newCapacity < 0) {
237: newCapacity = Integer.MAX_VALUE;
238: } else if (minSize > newCapacity) {
239: newCapacity = minSize;
240: }
241: char newBuffer[] = new char[newCapacity];
242: System.arraycopy(buffer, 0, newBuffer, 0, size);
243: buffer = newBuffer;
244: }
245:
246: /**
247: * Clear the contents.
248: */
249: public final void clear() {
250: size = 0;
251: }
252:
253: public void writeTo(PrintWriter writer) {
254: writer.write(buffer, 0, size);
255: }
256: }
|