001: /*
002: * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
003: * Copyright (C) 2005 - Javolution (http://javolution.org/)
004: * All rights reserved.
005: *
006: * Permission to use, copy, modify, and distribute this software is
007: * freely granted, provided that this notice is preserved.
008: */
009: package javolution.text;
010:
011: import j2me.lang.CharSequence;
012: import j2me.lang.Comparable;
013: import javolution.util.FastComparator;
014:
015: /**
016: * <p> This class represents a character sequence backed up by a
017: * <code>char</code> array. Instances of this class are mutable and
018: * are typically used/reused to hold temporary text (unlike
019: * <code>String</code> they do not forces object creation).</p>
020: *
021: * <p> Instances of this classes have the following properties:<ul>
022: *
023: * <li> They support equality or lexical comparison with any
024: * <code>CharSequence</code> (e.g. <code>String</code>).</li>
025: *
026: * <li> They have the same hashcode than <code>String</code> and can be
027: * used to retrieve data from maps for which the keys are
028: * <code>String</code> instances.</li>
029: *
030: * <li> They support fast conversions to primitive types
031: * (e.g. {@link #toBoolean() Boolean}, {@link #toInt int}).</li>
032: *
033: * </ul></p>
034: *
035: * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
036: * @version 4.0, June 16, 2006
037: */
038: public final class CharArray implements CharSequence, Comparable {
039:
040: /**
041: * Holds the character array.
042: */
043: private char[] _array;
044:
045: /**
046: * Holds the index of the first character.
047: */
048: private int _offset;
049:
050: /**
051: * Holds the length of char sequence.
052: */
053: private int _length;
054:
055: /**
056: * Holds the string representation of this CharArray (if known).
057: */
058: private String _asString;
059:
060: /**
061: * Default constructor.
062: */
063: public CharArray() {
064: }
065:
066: /**
067: * Creates a character array from the specified String.
068: *
069: * @param string the string source.
070: */
071: public CharArray(String string) {
072: _array = string.toCharArray();
073: _length = string.length();
074: _asString = string;
075: }
076:
077: /**
078: * Creates a character array from the specified character sequence.
079: *
080: * @param csq the character sequence source.
081: */
082: public CharArray(CharSequence csq) {
083: _length = csq.length();
084: _array = new char[_length];
085: for (int i = 0; i < _length;) {
086: _array[i] = csq.charAt(i++);
087: }
088: }
089:
090: /**
091: * Returns the underlying array (read-only).
092: * The array returned should not be modified (unfortunately there is
093: * no way to make an array immutable in Java).
094: *
095: * @return the underlying array.
096: */
097: public char[] array() {
098: return _array;
099: }
100:
101: /**
102: * Returns the length of this character sequence.
103: *
104: * @return the number of characters (16-bits Unicode) composing this
105: * character sequence.
106: */
107: public int length() {
108: return _length;
109: }
110:
111: /**
112: * Returns the offset of the first character in the underlying array.
113: *
114: * @return the offset of the first character.
115: */
116: public int offset() {
117: return _offset;
118: }
119:
120: /**
121: * Sets the underlying array of this CharArray.
122: *
123: * @param offset the new offset.
124: * @param array the new underlying array.
125: * @param length the new length.
126: * @return <code>this</code>
127: */
128: public CharArray setArray(char[] array, int offset, int length) {
129: _array = array;
130: _offset = offset;
131: _length = length;
132: _asString = null;
133: return this ;
134: }
135:
136: /**
137: * Returns the character at the specified index.
138: *
139: * @param index the index of the character starting at <code>0</code>.
140: * @return the character at the specified index of this character sequence.
141: * @throws IndexOutOfBoundsException if <code>((index < 0) ||
142: * (index >= length))</code>
143: */
144: public char charAt(int index) {
145: if ((index < 0) || (index >= _length))
146: throw new IndexOutOfBoundsException("index: " + index);
147: return _array[_offset + index];
148: }
149:
150: /**
151: * Returns a new character sequence that is a subsequence of this sequence.
152: *
153: * @param start the index of the first character inclusive.
154: * @param end the index of the last character exclusive.
155: * @return the character sequence starting at the specified
156: * <code>start</code> position and ending just before the specified
157: * <code>end</code> position.
158: * @throws IndexOutOfBoundsException if <code>(start < 0) || (end < 0) ||
159: * (start > end) || (end > this.length())</code>
160: */
161: public CharSequence subSequence(int start, int end) {
162: if ((start < 0) || (end < 0) || (start > end)
163: || (end > this .length()))
164: throw new IndexOutOfBoundsException();
165: CharArray chars = new CharArray();
166: chars._array = _array;
167: chars._offset = _offset + start;
168: chars._length = end - start;
169: return chars;
170: }
171:
172: /**
173: * Returns the offset within this character array of the first occurrence
174: * of the specified characters sequence searching forward from this
175: * character array {@link #offset()} to <code>offset() + length()</code>.
176: *
177: * @param csq a character sequence searched for.
178: * @return the offset of the specified character sequence in the range
179: * <code>[offset(), offset() + length()[</code>
180: * or <code>-1</code> if the character sequence is not found.
181: */
182: public final int offsetOf(CharSequence csq) {
183: final char c = csq.charAt(0);
184: final int csqLength = csq.length();
185: for (int i = _offset, end = _offset + _length - csqLength + 1; i < end; i++) {
186: if (_array[i] == c) { // Potential match.
187: boolean match = true;
188: for (int j = 1; j < csqLength; j++) {
189: if (_array[i + j] != csq.charAt(j)) {
190: match = false;
191: break;
192: }
193: }
194: if (match) {
195: return i;
196: }
197: }
198: }
199: return -1;
200: }
201:
202: /**
203: * Returns the offset within this character array of the first occurrence
204: * of the specified character searching forward from this
205: * character array {@link #offset()} to <code>offset() + length()</code>.
206: *
207: * @param c the character to search for.
208: * @return the offset of the specified character in the range
209: * <code>[offset(), offset() + length()[</code>
210: * or <code>-1</code> if the character is not found.
211: */
212: public final int offsetOf(char c) {
213: for (int i = _offset, end = _offset + _length; i < end; i++) {
214: if (_array[i] == c)
215: return i;
216: }
217: return -1;
218: }
219:
220: /**
221: * Returns the <code>String<code> corresponding to this character
222: * sequence. The <code>String</code> returned is always allocated on the
223: * heap and can safely be referenced elsewhere.
224: *
225: * @return the <code>java.lang.String</code> for this character sequence.
226: */
227: public String toString() {
228: if (_asString == null) {
229: _asString = new String(_array, _offset, _length);
230: }
231: return _asString;
232: }
233:
234: /**
235: * Returns the hash code for this {@link CharArray}.
236: *
237: * <p> Note: Returns the same hashCode as <code>java.lang.String</code>
238: * (consistent with {@link #equals})</p>
239: * @return the hash code value.
240: */
241: public int hashCode() {
242: if (_asString != null)
243: return _asString.hashCode();
244: int h = 0;
245: for (int i = 0, j = _offset; i < _length; i++) {
246: h = 31 * h + _array[j++];
247: }
248: return h;
249: }
250:
251: /**
252: * Compares this character sequence against the specified object
253: * (<code>String</code> or <code>CharSequence</code>).
254: *
255: * @param that the object to compare with.
256: * @return <code>true</code> if both objects represent the same sequence;
257: * <code>false</code> otherwise.
258: */
259: public boolean equals(Object that) {
260: if (that instanceof String) {
261: return equals((String) that);
262: } else if (that instanceof CharArray) {
263: return equals((CharArray) that);
264: } else if (that instanceof CharSequence) {
265: return equals((CharSequence) that);
266: } else {
267: return false;
268: }
269: }
270:
271: // Do not make public or String instances may not use equals(String)
272: private boolean equals(CharSequence chars) {
273: if (chars == null)
274: return false;
275: if (this ._length != chars.length())
276: return false;
277: for (int i = _length, j = _offset + _length; --i >= 0;) {
278: if (_array[--j] != chars.charAt(i))
279: return false;
280: }
281: return true;
282: }
283:
284: /**
285: * Compares this character array against the specified {@link CharArray}.
286: *
287: * @param that the character array to compare with.
288: * @return <code>true</code> if both objects represent the same sequence;
289: * <code>false</code> otherwise.
290: */
291: public boolean equals(CharArray that) {
292: if (this == that)
293: return true;
294: if (that == null)
295: return false;
296: if (this ._length != that._length)
297: return false;
298: final char[] thatData = that._array;
299: for (int i = that._offset + _length, j = _offset + _length; --j >= _offset;) {
300: if (_array[j] != thatData[--i])
301: return false;
302: }
303: return true;
304: }
305:
306: /**
307: * Compares this character array against the specified String.
308: * In case of equality, the CharArray keeps a reference to the
309: * String for future comparisons.
310: *
311: * @param str the string to compare with.
312: * @return <code>true</code> if both objects represent the same sequence;
313: * <code>false</code> otherwise.
314: */
315: public boolean equals(String str) {
316: if (_asString != null)
317: return (_asString == str) ? true
318: : _asString.equals(str) ? (_asString = str) == str
319: : false;
320: if (str == null)
321: return false;
322: if (_length != str.length())
323: return false;
324: for (int i = _length, j = _offset + _length; --i >= 0;) {
325: if (_array[--j] != str.charAt(i))
326: return false;
327: }
328: _asString = str;
329: return true;
330: }
331:
332: /**
333: * Compares this character array with the specified character
334: * sequence lexicographically.
335: *
336: * @param seq the character sequence to be compared.
337: * @return <code>{@link FastComparator#LEXICAL}.compare(this, seq)</code>
338: * @throws ClassCastException if the specifed object is not a
339: * <code>CharSequence</code>.
340: */
341: public int compareTo(Object seq) {
342: return ((FastComparator) FastComparator.LEXICAL).compare(this ,
343: seq);
344: }
345:
346: /**
347: * Returns the <code>boolean</code> represented by this character array.
348: *
349: * @return the corresponding <code>boolean</code> value.
350: * @throws NumberFormatException if this character sequence
351: * does not contain a parsable <code>boolean</code>.
352: */
353: public boolean toBoolean() {
354: int i = _offset;
355: if ((_length == 4) && (_array[i] == 't' || _array[i] == 'T')
356: && (_array[++i] == 'r' || _array[i] == 'R')
357: && (_array[++i] == 'u' || _array[i] == 'U')
358: && (_array[++i] == 'e' || _array[i] == 'E'))
359: return true;
360: if ((_length == 5) && (_array[i] == 'f' || _array[i] == 'F')
361: && (_array[++i] == 'a' || _array[i] == 'A')
362: && (_array[++i] == 'l' || _array[i] == 'L')
363: && (_array[++i] == 's' || _array[i] == 'S')
364: && (_array[++i] == 'e' || _array[i] == 'E'))
365: return false;
366: throw new IllegalArgumentException("Cannot parse " + this
367: + " as boolean");
368: }
369:
370: /**
371: * Returns the decimal <code>int</code> represented by this character array.
372: *
373: * @return <code>toInt(10)</code>
374: * @throws NumberFormatException if this character sequence
375: * does not contain a parsable <code>int</code>.
376: */
377: public int toInt() {
378: return TypeFormat.parseInt(this );
379: }
380:
381: /**
382: * Returns the <code>int</code> represented by this character array
383: * in the specified radix.
384: *
385: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
386: * @return the corresponding <code>int</code> value.
387: * @throws NumberFormatException if this character sequence
388: * does not contain a parsable <code>int</code>.
389: */
390: public int toInt(int radix) {
391: return TypeFormat.parseInt(this , radix);
392: }
393:
394: /**
395: * Returns the decimal <code>long</code> represented by this character
396: * array.
397: *
398: * @return the corresponding <code>long</code> value.
399: * @throws NumberFormatException if this character sequence
400: * does not contain a parsable <code>long</code>.
401: */
402: public long toLong() {
403: return TypeFormat.parseLong(this );
404: }
405:
406: /**
407: * Returns the decimal <code>long</code> represented by this character
408: * array in the specified radix.
409: *
410: * @param radix the radix (e.g. <code>16</code> for hexadecimal).
411: * @return the corresponding <code>long</code> value.
412: * @throws NumberFormatException if this character sequence
413: * does not contain a parsable <code>long</code>.
414: */
415: public long toLong(int radix) {
416: return TypeFormat.parseLong(this , radix);
417: }
418:
419: /**
420: * Returns the <code>float</code> represented by this character array.
421: *
422: * @return the corresponding <code>float</code> value.
423: * @return <code>TypeFormat.parseFloat(this)</code>
424: * @throws NumberFormatException if this character sequence
425: * does not contain a parsable <code>float</code>.
426: /*@JVM-1.1+@
427: public float toFloat() {
428: return TypeFormat.parseFloat(this);
429: }
430: /**/
431:
432: /**
433: * Returns the <code>double</code> represented by this character array.
434: *
435: * @return the corresponding <code>double</code> value.
436: * @throws NumberFormatException if this character sequence
437: * does not contain a parsable <code>double</code>.
438: /*@JVM-1.1+@
439: public double toDouble() {
440: return TypeFormat.parseDouble(this);
441: }
442: /**/
443:
444: }
|