001: package uk.org.ponder.stringutil;
002:
003: import uk.org.ponder.hashutil.CRC32;
004:
005: import uk.org.ponder.byteutil.ByteWrap;
006:
007: /**
008: * <code>CharWrap</code> provides the basic functionality of the Java
009: * <code>StringBuffer</code> class, only without the overhead of synchonised
010: * methods and with the ability (and corresponding danger) of direct access to
011: * the underlying array of characters.
012: *
013: * <br>
014: * <code>CharWrap</code> also integrates with the <code>StringVat</code> to
015: * map a string to an integer <code>StringVat</code> ID without causing an
016: * extra object creation.
017: *
018: * <br>
019: * At some point either the functionality needs to be added to "pre-intern"
020: * <code>String</code> objects from their <code>CharWrap</code>
021: * counterparts, or else <code>String</code> s should be removed completely
022: * from critical pathways through the code (e.g. <code>SAXalizer</code> and
023: * interfaces)
024: */
025:
026: public class CharWrap {
027: /** The default initial size for a CharWrap */
028: public static final int INITIAL_SIZE = 64;
029: /** An array holding the character data for this CharWrap */
030: public char[] storage;
031: /**
032: * The offset within the <code>storage</code> array at which the data
033: * represented by this CharWrap begins
034: */
035: public int offset = 0;
036: /** The length of the data represented by this CharWrap */
037: public int size = 0;
038:
039: /**
040: * Constructs a CharWrap with the default <code>INITIAL_SIZE</code>
041: */
042: public CharWrap() {
043: storage = new char[INITIAL_SIZE];
044: }
045:
046: /**
047: * Constructs a CharWrap with the specified initial size.
048: *
049: * @param initialsize
050: * The initial size of the character array.
051: */
052: public CharWrap(int initialsize) {
053: storage = new char[initialsize];
054: }
055:
056: /**
057: * Constructs a CharWrap with the specified initial contents, in an array
058: * allowing for expansion of factor 2.
059: *
060: * @param initialcontents
061: * The initial contents of the CharWrap
062: */
063: public CharWrap(String initialcontents) {
064: this (initialcontents.length() * 2);
065: append(initialcontents);
066: }
067:
068: /**
069: * Constructs a CharWrap around the supplied parameters, i.e. aliasing
070: * the supplied storage array.
071: *
072: * @param storage
073: * The array holding the character data.
074: * @param offset
075: * The offset within the array of the beginning of the character
076: * data.
077: * @param size
078: * The length of the character data.
079: */
080:
081: public CharWrap(char[] storage, int offset, int size) {
082: imbue(storage, offset, size);
083: }
084:
085: /**
086: * Imbues this CharWrap with the supplied parameters, i.e. aliasing the
087: * supplied storage array.
088: *
089: * @param storage
090: * The array holding the character data.
091: * @param offset
092: * The offset of the beginning of the character data within the
093: * array.
094: * @param size
095: * The length of the character data.
096: */
097:
098: public void imbue(char[] storage, int offset, int size) {
099: this .storage = storage;
100: this .offset = offset;
101: this .size = size;
102: }
103:
104: // copy the current contents into a new buffer if required to
105: // accommodate extra data
106: public final void ensureCapacity(int requiredsize) {
107: if (requiredsize + offset > storage.length) {
108: char[] newstorage = new char[requiredsize * 2];
109: System.arraycopy(storage, offset, newstorage, 0, size);
110: storage = newstorage;
111: offset = 0;
112: }
113: }
114:
115: /**
116: * Appends the characters in the supplied string onto this CharWrap.
117: *
118: * @param s
119: * A String holding the character data to be appended.
120: * @return A reference to this CharWrap.
121: */
122:
123: public final CharWrap append(String s) {
124: if (s == null)
125: s = "null";
126: int length = s.length();
127: ensureCapacity(size + length);
128: int start = size + offset;
129: if (length < 8) { // breakeven point optimised for JDK 1.4.2
130: for (int i = length - 1; i >= 0; --i) {
131: storage[start + i] = s.charAt(i);
132: }
133: } else {
134: s.getChars(0, length, storage, size + offset);
135: }
136: size += length;
137: return this ;
138: }
139:
140: public static final int PAD_LEFT = 0;
141: public static final int PAD_RIGHT = 1;
142:
143: public CharWrap appendPad(String s, int width, int dir) {
144: if (dir == PAD_RIGHT)
145: append(s);
146: for (int i = width - s.length(); i > 0; --i) {
147: append(' ');
148: }
149: if (dir == PAD_LEFT)
150: append(s);
151: return this ;
152: }
153:
154: /** Appends the "toString" value of the supplied Object to this CharWrap.
155: * The value will not be checked for <code>null</code>.
156: */
157: public CharWrap append(Object o) {
158: return append(o.toString());
159: }
160:
161: /**
162: * Appends the characters in the supplied CharWrap onto this CharWrap.
163: *
164: * @param toappend
165: * A CharWrap holding the character data to be appended.
166: * @return A reference to this CharWrap.
167: */
168:
169: public CharWrap append(CharWrap toappend) {
170: append(toappend.storage, toappend.offset, toappend.size);
171: return this ;
172: }
173:
174: /**
175: * Appends the specified character data onto this CharWrap.
176: *
177: * @param storage
178: * The array holding the character data to be appended.
179: * @param offset
180: * The offset within the array of the beginning of the character
181: * data.
182: * @param size
183: * The length of the character data.
184: * @return A reference to this CharWrap.
185: */
186:
187: public CharWrap append(char[] array, int start, int length) {
188: ensureCapacity(size + length);
189: System.arraycopy(array, start, storage, size + offset, length);
190: size += length;
191: return this ;
192: }
193:
194: /**
195: * Appends the specified character onto this CharWrap.
196: *
197: * @param c
198: * The character to be appended.
199: * @return A reference to this CharWrap.
200: */
201:
202: public CharWrap append(char c) {
203: ensureCapacity(size + 1);
204: storage[size + offset] = c;
205: size++;
206: return this ;
207: }
208:
209: /** Appends the specified character onto this CharWrap, without
210: * bounds checks.
211: */
212:
213: public final void appendFast(char c) {
214: storage[size + offset] = c;
215: size++;
216: }
217:
218: /**
219: * @param i
220: * Appends the specified integer rendered as a string onto this
221: * CharWrap.
222: * @return A reference to this CharWrap
223: */
224: public CharWrap append(int i) {
225: append(Integer.toString(i));
226: return this ;
227: }
228:
229: /**
230: * Clears this CharWrap by setting its size to zero.
231: */
232:
233: public CharWrap clear() {
234: size = 0;
235: return this ;
236: }
237:
238: // All methods after this point do not rebind the buffer
239:
240: /**
241: * Returns the first index at which a character appears in this CharWrap.
242: *
243: * @param tofind
244: * The character to find.
245: * @return The first index at which the specified character occurs, or
246: * <code>-1</code> if it does not appear.
247: */
248:
249: public int indexOf(char tofind) {
250: for (int i = 0; i < size; ++i) {
251: if (storage[i + offset] == tofind)
252: return i;
253: }
254: return -1;
255: }
256:
257: /**
258: * Returns the number of characters stored within this CharWrap.
259: *
260: * @return The size of the character data for this CharWrap.
261: */
262: public int size() {
263: return size;
264: }
265:
266: /**
267: * Returns the number of characters that can be appended to this CharWrap
268: * before it needs to reallocate its underlying character array.
269: *
270: * @return The spare capacity at the end of this CharWrap's character array.
271: */
272:
273: public int capacity() {
274: return storage.length - offset;
275: }
276:
277: /*
278: * Returns the character stored at the specified index of this CharWrap.
279: * @param index The index for which the stored character is required. @return
280: * The character at the specified index.
281: */
282: public char charAt(int index) {
283: return storage[index + offset];
284: }
285:
286: /**
287: * Converts this CharWrap to a new String object.
288: *
289: * @return A new String object holding the same character data as this
290: * CharWrap.
291: */
292: public String toString() {
293: return new String(storage, offset, size);
294: }
295:
296: public String toString(int start) {
297: return new String(storage, offset + start, size - start);
298: }
299:
300: /**
301: * Computes a hashcode for this CharWrap.
302: *
303: * @return An integer hashcode for this CharWap, currently by the CRC32
304: * algorithm.
305: */
306:
307: public int hashCode() {
308: return CRC32.eatquick(storage, offset, size);
309: }
310:
311: /**
312: * Converts this CharWrap to a new String object for debugging purposes. This
313: * string resolves all non-printable-ASCII characters into their hex
314: * equivalents.
315: *
316: * @return A String representing this CharWrap with only ASCII printable
317: * characters.
318: */
319:
320: public String toDebugString() {
321: CharWrap buildup = new CharWrap();
322: for (int i = 0; i < size; ++i) {
323: char charat = storage[offset + i];
324: if (charat >= 32 && charat < 127) {
325: buildup.append(charat);
326: } else
327: buildup.appendHexChar(charat);
328: }
329: return buildup.toString();
330: }
331:
332: /**
333: * Compares this CharWrap with another object. It is possible for a CharWrap
334: * to compare equal to a String with the same contents as well as to another
335: * CharWrap.
336: *
337: * @param othero
338: * An object to compare this CharWrap to.
339: * @return Returns <code>true</code> if the other object is either a String
340: * or a CharWrap with the same contents as this CharWrap.
341: */
342:
343: public boolean equals(Object othero) {
344: if (othero instanceof String) {
345: String other = (String) othero;
346: if (other.length() != size)
347: return false;
348: for (int i = 0; i < size; ++i) {
349: if (storage[i + offset] != other.charAt(i))
350: return false;
351: }
352: return true;
353: } else if (othero instanceof CharWrap) {
354: CharWrap other = (CharWrap) othero;
355: if (other.size != size)
356: return false;
357: for (int i = 0; i < size; ++i) {
358: if (storage[i + offset] != other.storage[i
359: + other.offset])
360: return false;
361: }
362: return true;
363: }
364: return false;
365: }
366:
367: public CharWrap appendHexChar(char toappend) {
368: int itohex = (int) toappend;
369: append(ByteWrap.toHex((itohex & 0xf000) >> 12));
370: append(ByteWrap.toHex((itohex & 0xf00) >> 8));
371: append(ByteWrap.toHex((itohex & 0xf0) >> 4));
372: append(ByteWrap.toHex((itohex & 0xf)));
373: return this ;
374: }
375:
376: /**
377: * Converts a character to a String holding a hexadecimal representation. The
378: * form of the representation is the HTML entity encoding the character as
379: * Unicode.
380: *
381: * @param tohex
382: * The character to be represented as hexadecimal.
383: * @return A string holding the character in HTML Unicode-entity form.
384: */
385:
386: public static final String charToHex(char tohex) {
387: CharWrap build = new CharWrap("&#x");
388: build.appendHexChar(tohex);
389: build.append(';');
390: return build.toString();
391: }
392: }
|