Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/simscomputing/util/MutableString.java


1   // Copyright 2000 Sims Computing, Inc.
2   // Licensed under the GNU Lesser General Public License.
3   
4   package com.simscomputing.util;
5   
6   import com.simscomputing.SoftwareFaultException;
7   import com.simscomputing.util.Debugger;
8   
9   import java.io.Serializable;
10  import java.io.UnsupportedEncodingException;
11  import java.util.ArrayList;
12  import java.util.Locale;
13  
14  /**
15  Implements a mutable string class. Implements all constructors and methods from
16  the String and StringBuffer classes. Some additional constructors and methods
17  are also implemented. This class is not thread safe.
18  
19  <p>
20  
21  <strong>Note:</strong> it has been discovered that the Sun implementation of
22  String and StringBuffer is written such that a StringBuffer can be converted
23  into a String without copying data. Consequently, the String API can
24  be used on a StringBuffer. This revelation may make this MutableString
25  class unnecessary.
26  
27  @author David Sims
28  @version $Revision: 1.1.1.1 $ $Date: 2000/02/21 21:22:36 $
29  */
30  public class MutableString implements Serializable {
31  
32    /**************/
33    /* attributes */
34    /**************/
35  
36    /** The mutable string is stored internally as an ArrayList. */
37    ArrayList stringArray = new ArrayList();
38  
39    /****************/
40    /* constructors */
41    /****************/
42  
43    /**
44    Constructs an empty MutableString.
45    */
46    public MutableString() {}
47  
48    /**
49    Constructs an empty FastString with capacity equal to 'length'.
50  
51    @throws NegativeArraySizeException if the length argument is less than 0.
52    */
53    public MutableString(int length) {
54      // make sure the string is big enough to hold 'length' elements
55      stringArray.ensureCapacity(length);
56    } // constructor
57  
58    /**
59    Constructs a MutableString that is initialized to 'string'.
60    */
61    public MutableString(String string) {
62      for (int index = 0; index < string.length(); ++index) {
63        stringArray.add(new Character(string.charAt(index)));
64      } // for index
65    } // constructor
66  
67    /**
68    Constructs a MutableString that is initialized to 'string'.
69    */
70    /*
71    public MutableString(StringBuffer string) {
72      for (int index = 0; index < string.length(); ++index) {
73        string.add(new Character(string.charAt(index)));
74      } // for index
75    } // constructor
76    */
77  
78    /**
79    Constructs a MutableString that is initialized to 'string'.
80    */
81    public MutableString(MutableString string) {
82      // deep copy the string
83      stringArray.ensureCapacity(string.length());
84      for (int index = 0; index < string.length(); ++index) {
85        stringArray.add(string.stringArray.get(index));
86      } // for index
87    } // constructor
88  
89    /**
90    Constructs a MutableString that is initialized to 'value'.
91  
92    @throws NullPointerException - if 'value' is null
93    */
94    public MutableString(char[] value) {
95      this(value, 0, value.length);
96    } // constructor
97  
98    /**
99    Constructs a MutableString that is initialized to
100   the string 'value[offset]..value[offset+count-1]'.
101 
102   @throws IndexOutOfBoundsException if the offset and count arguments index
103   characters outside the bounds of the value string.
104   @throws NullPointerException if value is null.
105   */
106   public MutableString(char[] value,
107                        int offset,
108                        int count) {
109     stringArray.ensureCapacity(count);
110     for (int index = offset; index < offset + count; ++index) {
111       stringArray.add(new Character(value[index]));
112     } // for index
113   } // constructor
114 
115   /**
116   Constructs a MutableString that is initialized to
117   the string 'bytes[offset]..bytes[offset+length-1]'
118   using the specified character encoding.
119 
120   @throws UnsupportedEncodingException if the named encoding is not supported
121   @throws IndexOutOfBoundsException if the offset and count arguments index characters
122   outside the bounds of the value string.
123   */
124   public MutableString(byte[] bytes,
125                        int offset,
126                        int length,
127                        String enc)
128                        throws UnsupportedEncodingException {
129     constructor(bytes, offset, length, enc);
130   } // constructor
131 
132   /**
133   Constructs a MutableString that is initialized to 'bytes'
134   using the specified character encoding.
135 
136   @throws UnsupportedEncodingException if the named encoding is not supported
137   */
138   public MutableString(byte[] bytes,
139                        String enc)
140                        throws UnsupportedEncodingException {
141     this(bytes, 0, bytes.length, enc);
142   } // constructor
143 
144   /**
145   Constructs a MutableString that is initialized to
146   the string 'bytes[offset]..bytes[offset+length-1]'.
147   */
148   public MutableString(byte[] bytes,
149                        int offset,
150                        int length) {
151     try {
152       constructor(bytes, offset, length, null);
153     } // try
154     catch (UnsupportedEncodingException e) {
155       throw new SoftwareFaultException("statement should not be reached");
156     } // catch
157   } // constructor
158 
159   /**
160   Constructs a MutableString that is initialized to 'bytes'.
161 
162   @param byte[] - the MutableString is initialized from these bytes
163   */
164   public MutableString(byte[] bytes) {
165     try {
166       constructor(bytes, 0, bytes.length, null);
167     } // try
168     catch (UnsupportedEncodingException e) {
169       throw new SoftwareFaultException("statement should not be reached");
170     } // catch
171   } // constructor
172 
173   /**
174   A method used in constructing the object.
175 
176   @param byte[] - initialize the object from these bytes
177   @param int - begin initializing from index 'offset'
178   @param int - take 'length' bytes from 'bytes'
179   @param String - the encoding used
180   */
181   private void constructor(byte[] bytes,
182                            int offset,
183                            int length,
184                            String enc)
185                throws UnsupportedEncodingException {
186     // in case a non-constructor method calls this method
187     stringArray.clear();
188     
189     if (enc == null) {
190       // use default encoding
191       stringArray.ensureCapacity(length);
192       for (int index = offset; index < offset + length; ++index) {
193         stringArray.add(new Character( (char) bytes[index]));
194       } // for index
195     } // if
196     else {
197       // use supplied encoding via the String class
198       // note: is there a way we can do encodings directly?
199       String encodedString = new String(bytes, offset, length, enc);
200       for (int index = 0; index < encodedString.length(); ++index) {
201         stringArray.add(new Character( (char) encodedString.charAt(index)));
202       } // for index
203     } // else
204   } // constructor()
205 
206   /***********/
207   /* methods */
208   /***********/
209 
210   /**
211   Returns the size of the string.
212 
213   @return int - the size of the string
214   */
215   public int length() {
216     return stringArray.size();
217   } // length()
218 
219   /**
220   Returns string[index].
221 
222   @return char - the character at position 'index'
223   */
224   public char charAt(int index) {
225     if (index < 0 || index >= stringArray.size()) {
226       throw new StringIndexOutOfBoundsException();
227     } // if
228 
229     return ((Character) stringArray.get(index)).charValue();
230   } // charAt()
231 
232   /**
233   Copies MutableString[srcBegin..srcEnd - 1] into 'dst', beginning at offset
234   'dstBegin'.
235 
236   @param int - the beginning offset into this object
237   @param int - the ending offset into this object
238   @param char[] - the container for the results
239   @param int - the offset into 'dst' to begin inserting the results
240 
241   IndexOutOfBoundsException - If any of the following is true:
242   srcBegin is negative.
243   srcBegin is greater than srcEnd
244   srcEnd is greater than the length of this string
245   dstBegin is negative
246   dstBegin+(srcEnd-srcBegin) is larger than dst.length
247   NullPointerException - if dst is null
248   */
249   public void getChars(int srcBegin,
250                        int srcEnd,
251                        char[] dst,
252                        int dstBegin) {
253     // note: the following 'if' statement would throw a NullPointerException
254     // if 'dst' were null. Checked here anyway; the code is easier to
255     // understand.
256     if (dst == null) {
257       throw new NullPointerException();
258     } // if
259 
260     if (srcBegin < 0 || srcBegin > srcEnd || srcEnd > stringArray.size() ||
261         dstBegin < 0 || dstBegin + (srcEnd - srcBegin) > dst.length) {
262       throw new IndexOutOfBoundsException();
263     } // if
264 
265     int dstIndex = dstBegin;
266     for (int index = srcBegin; index < srcEnd; ++index) {
267       dst[dstIndex] = ((Character) stringArray.get(index)).charValue();
268       ++dstIndex;
269     } // for index
270   } // getChars()
271 
272   /**
273   Returns the MutableString in byte[] form.
274 
275   @param String - the encoding to use
276   @return byte[] - the string as bytes using encoding 'enc'
277   */
278   public byte[] getBytes(String enc)
279                          throws UnsupportedEncodingException {
280     byte[] result = new byte[stringArray.size()];
281     for (int index = 0; index < stringArray.size(); ++index) {
282       result[index] = (byte) ((Character) stringArray.get(index)).charValue();
283     } // for index
284 
285     if (enc != null) {
286       String s = new String(result);
287       return s.getBytes(enc);
288     } // else
289 
290     return result;
291   } // getBytes()
292 
293   /**
294   Returns the MutableString in byte[] form.
295 
296   @return byte[] - the string as bytes
297   */
298   public byte[] getBytes() {
299     try {
300       return getBytes(null);
301     } // try
302     catch (UnsupportedEncodingException e) {
303       throw new SoftwareFaultException("statement should not be reached");
304     } // catch
305   } // getBytes()
306 
307   /**
308   Indicates whether 'anObject' is equivalent to this object. 'anObject' must
309   be an instance of MutableString.
310 
311   @param Object - the object to compare to this object
312   @return boolean - whether the objects match
313   */
314   public boolean equals(Object anObject) {
315     /*
316     if (anObject instanceof String) {
317       return equals(new MutableString( (String) anObject), true);
318     } // if
319     */
320 
321     if (!(anObject instanceof MutableString)) {
322       return false;
323     } // if
324 
325     // 'anObject' is a MutableString
326     MutableString aString = (MutableString) anObject;
327     return equals(aString, true);
328   } // equals()
329 
330   /**
331   Indicates whether 'anObject' is equivalent to this object.
332 
333   @param String - the object to compare to this object
334   @return boolean - whether the objects match
335   */
336   public boolean equals(String anObject) {
337     return equals(new MutableString( (String) anObject), true);
338   } // equals()
339 
340   /**
341   Indicates whether 'anotherString' equals this object. Case is ignored.
342 
343   @param MutableString - the MutableString to compare to this object
344   @return boolean - whether the MutableStrings match
345   */
346   public boolean equalsIgnoreCase(MutableString anotherString) {
347     return equals(anotherString, false);
348   } // equalsIgnoreCase()
349 
350   /**
351   Indicates whether 'anotherString' equals this object. Case is ignored.
352 
353   @param String - the string to compare to this object
354   @return boolean - whether the objects match
355   */
356   public boolean equalsIgnoreCase(String anotherString) {
357     return equalsIgnoreCase(new MutableString(anotherString));
358   } // equalsIgnoreCase()
359 
360   /**
361   Indicates whether 'anotherString' equals this object.
362 
363   @param MutableString - the string to compare to this object
364   @param boolean - whether the comparison should match case
365   @return boolean - whether the objects match
366   */
367   private boolean equals(MutableString anotherString, boolean matchCase) {
368     if (anotherString.stringArray.size() != stringArray.size()) {
369       return false;
370     } // if
371 
372     // check if all the characters match
373     char leftChar;
374     char rightChar;
375 
376     for (int index = 0; index < stringArray.size(); ++index) {
377       if (matchCase) {
378         leftChar = ((Character) stringArray.get(index)).charValue();
379         rightChar = ((Character) anotherString.stringArray.get(index)).charValue();
380       } // if
381       else {
382         leftChar = Character.toLowerCase( ((Character) stringArray.get(index)).charValue());
383         rightChar = Character.toLowerCase( ((Character) anotherString.stringArray.get(index)).charValue());
384       } // else
385 
386       if (leftChar != rightChar) {
387         // at least one pair of characters don't match
388         return false;
389       } // if
390     } // for index
391 
392     // all the characters match
393     return true;
394   } // equals()
395 
396   /**
397   Returns an integer that represents how the MutableString compares
398   to 'anotherString'. The comparison result is computed as follows:
399 
400   If length() != anotherString.length(), result is
401   length() - anotherString.length().
402   Otherwise:
403   If this object exactly matches 'anotherString', result is 0.
404   Otherwise:
405   For the first character position where the characters from the two strings
406   do not match, result is 'string[position] - anotherString[position]'.
407 
408   @param MutableString - the string to compare to this object
409   @return int - the comparison result
410   @throws NullPointerException - if anotherString is null.
411   */
412   public int compareTo(MutableString anotherString) {
413     return compareTo(anotherString, true);
414   } // compareTo()
415 
416   /**
417   Returns an integer that represents how the MutableString compares
418   to 'anotherString'. The comparison result is computed as follows:
419 
420   If length() != anotherString.length(), result is
421   length() - anotherString.length().
422   Otherwise:
423   If this object exactly matches 'anotherString', result is 0.
424   Otherwise:
425   For the first character position where the characters from the two strings
426   do not match, result is 'string[position] - anotherString[position]'.
427 
428   @param String - the string to compare to this object
429   @return int - the comparison result
430   @throws NullPointerException - if anotherString is null.
431   */
432   public int compareTo(String anotherString) {
433     return compareTo(new MutableString(anotherString), true);
434   } // compareTo()
435 
436   /**
437   Returns an integer that represents how the MutableString compares
438   to 'anotherString'. The comparison result is computed as follows:
439 
440   If 'anotherString' is not a String, result is 0.
441   Otherwise:
442   If length() != anotherString.length(), result is
443   length() - anotherString.length().
444   Otherwise:
445   If this object exactly matches 'anotherString', result is 0.
446   Otherwise:
447   For the first character position where the characters from the two strings
448   do not match, result is 'string[position] - anotherString[position]'.
449 
450   @param MutableString - the string to compare to this object
451   @return int - the comparison result
452   @throws NullPointerException - if anotherString is null.
453   */
454   public int compareTo(Object anotherString) {
455     if (!(anotherString instanceof MutableString)) {
456       return 0;
457     } // if
458 
459     MutableString string = (MutableString) anotherString;
460     return compareTo(string, true);
461   } // compareTo()
462 
463   /**
464   Returns an integer that represents how the MutableString compares
465   to 'anotherString'. Caes is ignored. The comparison result is computed as
466   follows:
467 
468   If length() != anotherString.length(), result is
469   length() - anotherString.length().
470   Otherwise:
471   If this object exactly matches 'anotherString', result is 0.
472   Otherwise:
473   For the first character position where the characters from the two strings
474   do not match, result is 'string[position] - anotherString[position]'.
475 
476   @param MutableString - the string to compare to this object
477   @return int - the comparison result
478   @throws NullPointerException - if anotherString is null.
479   */
480   public int compareToIgnoreCase(MutableString string) {
481     return compareTo(string, false);
482   } // compareToIgnoreCase()
483 
484   /**
485   Returns an integer that represents how the MutableString compares
486   to 'anotherString'. Case is ignored. The comparison result is computed as
487   follows:
488 
489   If length() != anotherString.length(), result is
490   length() - anotherString.length().
491   Otherwise:
492   If this object exactly matches 'anotherString', result is 0.
493   Otherwise:
494   For the first character position where the characters from the two strings
495   do not match, result is 'string[position] - anotherString[position]'.
496 
497   @param String - the string to compare to this object
498   @return int - the comparison result
499   @throws NullPointerException - if anotherString is null.
500   */
501   public int compareToIgnoreCase(String string) {
502     return compareTo(new MutableString(string), false);
503   } // compareToIgnoreCase()
504 
505   /**
506   Returns an integer that represents how the MutableString compares
507   to 'anotherString'. The comparison result is computed as follows:
508 
509   If length() != anotherString.length(), result is
510   length() - anotherString.length().
511   Otherwise:
512   If this object exactly matches 'anotherString', result is 0.
513   Otherwise:
514   For the first character position where the characters from the two strings
515   do not match, result is 'string[position] - anotherString[position]'.
516 
517   @param MutableString - the string to compare to this object
518   @return int - the comparison result
519   @throws NullPointerException - if anotherString is null.
520   */
521   private int compareTo(MutableString anotherString, boolean matchCase) {
522     if (stringArray.size() != anotherString.length()) {
523       return stringArray.size() - anotherString.length();
524     } // if
525 
526     // check if all the characters match
527     char leftChar;
528     char rightChar;
529 
530     for (int index = 0; index < stringArray.size(); ++index) {
531       if (matchCase) {
532         leftChar = ((Character) stringArray.get(index)).charValue();
533         rightChar = ((Character) anotherString.stringArray.get(index)).charValue();
534       } // if
535       else {
536         leftChar = Character.toLowerCase( ((Character) stringArray.get(index)).charValue());
537         rightChar = Character.toLowerCase( ((Character) anotherString.stringArray.get(index)).charValue());
538       } // else
539 
540       if (leftChar != rightChar) {
541         // at least one pair of characters don't match
542         if (matchCase) {
543           return ((Character) stringArray.get(index)).charValue() -
544                  ((Character) anotherString.stringArray.get(index)).charValue();
545         } // if
546         else {
547           return leftChar - rightChar;
548         } // else
549       } // if
550     } // for index
551 
552     // all the characters match
553     return 0;
554   } // compareTo()
555 
556   /**
557   Indicates whether the MutableString[toffset..toffset + len] matches
558   other[ooffset..ooffset + len], ignoring case.
559 
560   @param int - the offset into this object
561   @param MutableString - the string to compare against this object
562   @param int - the offset into 'other'
563   @param int - the number of characters to compare
564   @returns boolean - whether the two strings match
565   @throws NullPointerException - if 'other' is null
566   */
567   public boolean regionMatches(int toffset,
568                                String other,
569                                int ooffset,
570                                int len) {
571     return regionMatches(toffset, new MutableString(other), ooffset, len);
572   } // regionMatches()
573 
574   /**
575   Indicates whether the MutableString[toffset..toffset + len] matches
576   other[ooffset..ooffset + len], ignoring case.
577 
578   @param int - the offset into this object
579   @param MutableString - the string to compare against this object
580   @param int - the offset into 'other'
581   @param int - the number of characters to compare
582   @returns boolean - whether the two strings match
583   @throws NullPointerException - if 'other' is null
584   */
585   public boolean regionMatches(int toffset,
586                                MutableString other,
587                                int ooffset,
588                                int len) {
589     return regionMatches(false, toffset, other, ooffset, len);
590   } // regionMatches()
591 
592   /**
593   Indicates whether the MutableString[toffset..toffset + len] matches
594   other[ooffset..ooffset + len], ignoring case.
595 
596   @param boolean - whether to ignore case
597   @param int - the offset into this object
598   @param String - the string to compare against this object
599   @param int - the offset into 'other'
600   @param int - the number of characters to compare
601   @returns boolean - whether the two strings match
602   @throws NullPointerException - if 'other' is null
603   */
604   public boolean regionMatches(boolean ignoreCase,
605                                int toffset,
606                                String other,
607                                int ooffset,
608                                int len) {
609     return regionMatches(ignoreCase, toffset, new MutableString(other),
610                          ooffset, len);
611   } // regionMatches()
612 
613   /**
614   Indicates whether the MutableString[toffset..toffset + len] matches
615   'other[ooffset..ooffset + len]', ignoring case.
616 
617   @param boolean - whether to ignore case
618   @param int - the offset into this object
619   @param MutableString - the string to compare against this object
620   @param int - the offset into 'other'
621   @param int - the number of characters to compare
622   @returns boolean - whether the two strings match
623   @throws NullPointerException - if 'other' is null
624   */
625   public boolean regionMatches(boolean ignoreCase,
626                                int toffset,
627                                MutableString other,
628                                int ooffset,
629                                int len) {
630     if (!(toffset + len <= stringArray.size() && ooffset + len <= other.stringArray.size())) {
631       return false;
632     } // if
633 
634     // check if all the characters match
635     char leftChar;
636     char rightChar;
637 
638     for (int count = 0; count < len; ++count) {
639       if (!ignoreCase) {
640         leftChar = ((Character) stringArray.get(toffset + count)).charValue();
641         rightChar = ((Character) other.stringArray.get(ooffset + count)).charValue();
642       } // if
643       else {
644         leftChar = Character.toLowerCase( ((Character) stringArray.get(toffset + count)).charValue());
645         rightChar = Character.toLowerCase( ((Character) other.stringArray.get(ooffset +
646                                                                         count)).charValue());;
647       } // else
648 
649       if (leftChar != rightChar) {
650         // at least one pair of characters don't match
651         return false;
652       } // if
653     } // for index
654 
655     // all the characters match
656     return true;
657 
658   } // regionMatches()
659 
660   /**
661   Indicates whether the MutableString begins with
662   'prefix[toffset..prefix.length() - 1]'.
663 
664   @param MutableString - the prefix to check
665   @returns boolean - whether the string begins with 'prefix'
666   @throws NullPointerException - if prefix is null
667   */
668   public boolean startsWith(String prefix,
669                             int toffset) {
670     return startsWith(new MutableString(prefix), toffset);
671   } // startsWith()
672 
673   /**
674   Indicates whether the MutableString begins with
675   'prefix[toffset..prefix.length() - 1]'.
676 
677   @param MutableString - the prefix to check
678   @returns boolean - whether the string begins with 'prefix'
679   @throws NullPointerException - if prefix is null
680   */
681   public boolean startsWith(MutableString prefix,
682                             int toffset) {
683     if (stringArray.size() < prefix.stringArray.size()) {
684       return false;
685     } // if
686 
687     return regionMatches(0, prefix, toffset, prefix.stringArray.size());
688   } // startsWith()
689 
690   /**
691   Indicates whether the MutableString begins with 'prefix'.
692 
693   @param String - the prefix to check
694   @returns boolean - whether the string begins with 'prefix'
695   @throws NullPointerException - if prefix is null
696   */
697   public boolean startsWith(String prefix) {
698     return startsWith(new MutableString(prefix));
699   } // startsWith()
700 
701   /**
702   Indicates whether the MutableString begins with 'prefix'.
703 
704   @param MutableString - the prefix to check
705   @returns boolean - whether the string begins with 'prefix'
706   @throws NullPointerException - if prefix is null
707   */
708   public boolean startsWith(MutableString prefix) {
709     return startsWith(prefix, 0);
710   } // startsWith()
711 
712   /**
713   Indicates whether the MutableString ends with 'suffix'.
714 
715   @param MutableString - the suffix to check
716   @returns boolean - whether the string ends with 'suffix'
717   @throws NullPointerException - if suffix is null.
718   */
719   public boolean endsWith(MutableString suffix) {
720     if (stringArray.size() < suffix.stringArray.size()) {
721       return false;
722     } // if
723 
724     // regionMatches() performs the comparison
725     return regionMatches(stringArray.size() - suffix.stringArray.size(),
726                          suffix,
727                          0,
728                          suffix.stringArray.size());
729   } // endsWith()
730 
731   /**
732   Indicates whether the string ends with 'suffix'.
733 
734   @param String - the suffix to check
735   @returns boolean - whether the string ends with 'suffix'
736   */
737   public boolean endsWith(String suffix) {
738     return endsWith(new MutableString(suffix));
739   } // endsWith()
740 
741   /**
742   Computes a hash code on the string.
743   
744   @returns int - s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
745   */
746   public int hashCode() {
747     // s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
748 
749     // note: check if 'return new String(ms.toString()).hashCode()' is faster
750     int result = 0;
751     int power;
752 
753     for (int index = 0; index < stringArray.size(); ++index) {
754       // compute 31^(length() - index - 1)
755       power = 1;
756       for (int j = 0; j < stringArray.size() - index - 1; ++j) {
757         power *= 31;
758       } // for j
759 
760       // result += ((Character) stringArray.get(index)).charValue() * ((int) Math.pow(31.0, ));
761       result += ((Character) stringArray.get(index)).charValue() * power;
762     } // index
763 
764     return result;
765   } // hashCode()
766 
767   /**
768   Returns the index, if it exists, of the first occurence of 'ch' in
769   the string.
770 
771   @param int - the character to search for
772   @return int - the index, if it exists, of the first occurence of 'ch'. If the
773   character does not exist, -1 is returned
774   */
775   public int indexOf(int ch) {
776     return indexOf(ch, 0);
777   } // indexOf()
778 
779   /**
780   Returns the index, if it exists, of the first occurence of 'ch' in
781   the string starting at position 'fromIndex' in the string.
782 
783   @param int - the character to search for
784   @return int - the index, if it exists, of the first occurence of 'ch'. If the
785   character does not exist, -1 is returned
786   */
787   public int indexOf(int ch,
788                      int fromIndex) {
789     for (int index = fromIndex; index < stringArray.size(); ++index) {
790       if ( ((Character) stringArray.get(index)).charValue() == ch) {
791         return index;
792       } // if
793     } // for index
794 
795     return -1;
796   } // indexOf()
797 
798   /**
799   Returns the index, if it exists, of the last occurence of 'ch' in
800   the string.
801 
802   @param ch - the character to search for
803   @return int - the index, if it exists, of the last occurence of 'ch'. If the
804   character does not exist, -1 is returned
805   */
806   public int lastIndexOf(int ch) {
807     return lastIndexOf(ch, 0);
808   } // lastIndexOf()
809 
810   /**
811   Returns the index, if it exists, of the last occurence of 'ch' in
812   the string starting at position 'fromIndex' in the string.
813 
814   @param char - the character to search for
815   @return int - the index, if it exists, of the last occurence of 'ch'. If the
816   character does not exist, -1 is returned
817   */
818   public int lastIndexOf(int ch,
819                          int fromIndex) {
820     int result = -1;
821 
822     for (int index = fromIndex; index < stringArray.size(); ++index) {
823       if ( ((Character) stringArray.get(index)).charValue() == ch) {
824         result = index;
825       } // if
826     } // for index
827 
828     return result;
829   } // lastIndexOf()
830 
831   /**
832   Returns the index, if it exists, of the first occurence of 'str' in
833   the string.
834 
835   @param MutableString - the string to search for
836   @return int - the index, if it exists, of the first occurence of 'str'. If the
837   character does not exist, -1 is returned
838   */
839   public int indexOf(MutableString str) {
840     return indexOf(str.toString());
841   } // indexOf()
842 
843   /**
844   Returns the index, if it exists, of the first occurence of 'str' in
845   the string.
846 
847   @param String - the string to search for
848   @return int - the index, if it exists, of the first occurence of 'str'. If the
849   character does not exist, -1 is returned
850   */
851   public int indexOf(String str) {
852     return indexOf(str, 0, false);
853   } // indexOf()
854 
855   /**
856   Returns the index, if it exists, of the first occurence of 'str' in
857   the string beginning at offset 'fromIndex'.
858 
859   @param MutableString - the string to search for
860   @return int - the index, if it exists, of the first occurence of 'str'
861   beginning at offset 'fromIndex'. If the character does not exist, -1 is
862   returned
863   */
864   public int indexOf(MutableString str, int fromIndex) {
865     return indexOf(str.toString(), fromIndex);
866   } // indexOf()
867 
868   /**
869   Returns the index, if it exists, of the first occurence of 'str' in
870   the string beginning at offset 'fromIndex'.
871 
872   @param String - the string to search for
873   @return int - the index, if it exists, of the first occurence of 'str'
874   beginning at offset 'fromIndex'. If the character does not exist, -1 is
875   returned
876   */
877   public int indexOf(String str,
878                      int fromIndex) {
879     return indexOf(str, fromIndex, false);
880   } // indexOf()
881 
882   /**
883   Returns the index, if it exists, of the last occurence of 'str' in
884   the string.
885 
886   @param MutableString - the string to search for
887   @return int - the index, if it exists, of the last occurence of 'str'. If the
888   character does not exist, -1 is returned
889   */
890   public int lastIndexOf(MutableString str) {
891     return lastIndexOf(str.toString());
892   } // lastIndexOf()
893 
894   /**
895   Returns the index, if it exists, of the last occurence of 'str' in
896   the string.
897 
898   @param String - the string to search for
899   @return int - the index, if it exists, of the last occurence of 'str'. If the
900   character does not exist, -1 is returned
901   */
902   public int lastIndexOf(String str) {
903     return indexOf(str, 0, true);
904   } // lastIndexOf()
905 
906   /**
907   Returns the index, if it exists, of the last occurence of 'str' in
908   the string beginning at offset 'fromIndex'.
909 
910   @param MutableString - the string to search for
911   @return int - the index, if it exists, of the last occurence of 'str'
912   beginning at offset 'fromIndex'. If the character does not exist, -1 is
913   returned
914   */
915   public int lastIndexOf(MutableString str,
916                          int fromIndex) {
917     return lastIndexOf(str.toString(), fromIndex);
918   } // lastIndexOf()
919 
920   /**
921   Returns the index, if it exists, of the last occurence of 'str' in
922   the string beginning at offset 'fromIndex'.
923 
924   @param String - the string to search for
925   @return int - the index, if it exists, of the last occurence of 'str'
926   beginning at offset 'fromIndex'. If the character does not exist, -1 is
927   returned
928   */
929   public int lastIndexOf(String str,
930                          int fromIndex) {
931     return indexOf(str, fromIndex, true);
932   } // lastIndexOf()
933 
934   /**
935   Returns the index, if it exists, of the first or last occurence of 'str' in
936   the string beginning at offset 'fromIndex'.
937 
938   @param String - the string to search for
939   @param int - the index to begin searching from
940   @param boolean - whether to look for the first or last occurence
941   @return int - the index, if it exists, of the first or last occurence of 'str'
942   beginning at offset 'fromIndex'. If the character does not exist, -1 is
943   returned
944   */
945   private int indexOf(String str, int fromIndex, boolean last) {
946     // note: use a better string matching algorithm
947 
948     int matchIndex = -1;
949 
950     for (int index = fromIndex; index < stringArray.size(); ++index) {
951       boolean matched = true;
952 
953       // check if there's enough room left in 'string' to match 'str'
954       if (index + str.length() < stringArray.size()) {
955         int count = 0;
956         for (int j = 0; j < str.length(); ++j) {
957           if ( ((Character) stringArray.get(index + j)).charValue() !=
958                str.charAt(j)) {
959             matched = false;
960           } // if
961         } // for j
962       } // if
963       else {
964         matched = false;
965       } // else
966 
967       if (matched) {
968         if (last) {
969           matchIndex = index;
970         } // if
971         else {
972           return index;
973         } // else
974       } // if
975     } // index
976 
977     return matchIndex;
978   } // indexOf()
979 
980   /**
981   Returns string[beginIndex..length() - 1] as a new MutableString.
982 
983   @returns MutableString - a new MutableString
984   @throws IndexOutOfBoundsException - if beginIndex is negative or larger than
985   the length of this String object.
986   */
987   public MutableString substring(int beginIndex) {
988     return substring(beginIndex, stringArray.size());
989   } // substring()
990 
991   /**
992   Returns string[beginIndex..endIndex - 1] as a new MutableString.
993 
994   @returns MutableString - a new MutableString 
995   @throws IndexOutOfBoundsException - if the beginIndex is negative, or
996   endIndex is larger than the length of this String object, or  beginIndex is
997   larger than endIndex.
998   */
999   public MutableString substring(int beginIndex,
1000                                 int endIndex) {
1001    char[] stringArray = new char[endIndex - beginIndex];
1002
1003    int j = 0;
1004    for (int index = beginIndex; index < endIndex; ++index) {
1005      stringArray[j] = ((Character) this.stringArray.get(index)).charValue();
1006      ++j;
1007    } // for index
1008    return new MutableString(stringArray);
1009  } // substring()
1010
1011  /**
1012  Concatenates 'str' to the MutableString.
1013
1014  @param MutableString - the string to concatenate
1015  @returns MutableString - a reference to this object
1016  @throws NullPointerException - if str is null
1017  */
1018  public MutableString concat(MutableString str) {
1019    return concat(str.toString());
1020  } // concat()
1021
1022  /**
1023  Concatenates 'str' to the MutableString.
1024
1025  @param String - the string to concatenate
1026  @returns MutableString - a reference to this object
1027  @throws NullPointerException - if str is null
1028  */
1029  public MutableString concat(String str) {
1030    // make the string big enough to accommodate the new string
1031    stringArray.ensureCapacity(stringArray.size() + str.length());
1032
1033    // concatenate the new string
1034    for (int index = 0; index < str.length(); ++index) {
1035      stringArray.add(new Character(str.charAt(index)));
1036    } // for index
1037
1038    return this;
1039  } // concat()
1040
1041  /**
1042  Replaces all instances of 'oldChar' with 'newChar'.
1043
1044  @param char - the old character
1045  @param char - the new character that replaces the old character
1046  @returns MutableString - a reference to this object
1047  */
1048  public MutableString replace(char oldChar,
1049                               char newChar) {
1050    for (int index = 0; index < stringArray.size(); ++index) {
1051      if ( ((Character) stringArray.get(index)).charValue() == oldChar) {
1052        stringArray.set(index, new Character(newChar));
1053      } // if
1054    } // for index
1055
1056    return this;
1057  } // replace()
1058
1059  /**
1060  Converts all characters in the string to upper case.
1061
1062  @param Locale - convert characters using the specified locale
1063  @returns MutableString - a reference to the string
1064  */
1065  public MutableString toLowerCase(Locale locale) {
1066    if (locale == null) {
1067      // use the default locale
1068
1069      char ch;
1070      Character character;
1071      for (int index = 0; index < stringArray.size(); ++index) {
1072        ch = ((Character) stringArray.get(index)).charValue();
1073        character = new Character(Character.toLowerCase(ch));
1074        stringArray.set(index, character);
1075      } // for index
1076    } // if
1077    else {
1078      // use the specified locale
1079
1080      String s = new String(toString());
1081
1082      // convert to lower case using the specified locale
1083      s.toLowerCase(locale);
1084      // make sure the length of the string didn't change
1085      Debugger.assert(s.length() == length());
1086
1087      Character character;
1088      for (int index = 0; index < stringArray.size(); ++index) {
1089        character = new Character(s.charAt(index));
1090        stringArray.set(index, character);
1091      } // for index
1092    } // else
1093
1094    return this;
1095  } // toLowerCase()
1096
1097  /**
1098  Converts all characters in the string to lower case.
1099
1100  @returns MutableString - a reference to the string
1101  */
1102  public MutableString toLowerCase() {
1103    return toLowerCase(null);
1104  } // toLowerCase()
1105
1106  /**
1107  Converts all characters in the string to upper case.
1108
1109  @param Locale - convert characters using the specified locale
1110  @returns MutableString - a reference to the string
1111  */
1112  public MutableString toUpperCase(Locale locale) {
1113    if (locale == null) {
1114      // use the default locale
1115
1116      char ch;
1117      Character character;
1118      for (int index = 0; index < stringArray.size(); ++index) {
1119        ch = ((Character) stringArray.get(index)).charValue();
1120        character = new Character(Character.toUpperCase(ch));
1121        stringArray.set(index, character);
1122      } // for index
1123    } // if
1124    else {
1125      // use the specified locale
1126
1127      String s = new String(toString());
1128
1129      // convert to lower case using the specified locale
1130      s.toUpperCase(locale);
1131      // make sure the length of the string didn't change
1132      Debugger.assert(s.length() == length());
1133
1134      Character character;
1135      for (int index = 0; index < stringArray.size(); ++index) {
1136        character = new Character(s.charAt(index));
1137        stringArray.set(index, character);
1138      } // for index
1139    } // else
1140
1141    return this;
1142  } // toUpperCase()
1143
1144  /**
1145  Converts all characters in the string to upper case.
1146
1147  @returns MutableString - a reference to the string
1148  */
1149  public MutableString toUpperCase() {
1150    return toUpperCase(null);
1151  } // toUpperCase()
1152
1153  /**
1154  Removes leading and trailing whitespace.
1155
1156  @returns a reference to the object
1157  */
1158  public MutableString trim() {
1159    boolean removed;
1160
1161    // remove leading whitespace
1162    do {
1163      removed = false;
1164      if ( ((Character) stringArray.get(0)).charValue() <= '\u0020') {
1165        stringArray.remove(0);
1166        removed = true;
1167      } // if
1168    } while (removed);
1169
1170    // remove trailing whitespace
1171    removed = true;
1172    while (removed && stringArray.size() > 0) {
1173      removed = false;
1174      if ( ((Character) stringArray.get(stringArray.size() - 1)).charValue() <= '\u0020') {
1175        stringArray.remove(stringArray.size() - 1);
1176        removed = true;
1177      } // if
1178    } // while
1179
1180    return this;
1181  } // trim()
1182
1183  /**
1184  Converts the MutableString to a String
1185
1186  @returns String - the String representation of the MutableString
1187  */
1188  public String toString() {
1189    return new String(toCharArray());
1190  } // toString()
1191
1192  /**
1193  Converts the MutableString to a character string
1194
1195  @returns String - the char[] representation of the MutableString
1196  */
1197  public char[] toCharArray() {
1198    Object[] characters = stringArray.toArray();
1199
1200    char[] chars = new char[characters.length];
1201
1202    for (int index = 0; index < chars.length; ++index) {
1203      chars[index] = ((Character) characters[index]).charValue();
1204    } // for index
1205
1206    return chars;
1207  } // toCharArray()
1208
1209  /**
1210  Creates a MutableString from the string representation of 'obj'.
1211
1212  @param Object - the object to create a string from
1213  @returns MutableString - the MutableString representation of 'obj'
1214  */
1215  public static MutableString valueOf(Object obj) {
1216    if (obj == null) {
1217      return new MutableString("null");
1218    } // if
1219
1220    return new MutableString(obj.toString());
1221  } // valueOf()
1222
1223  /**
1224  Creates a MutableString from the string representation of 'data'.
1225
1226  @param char[] - the object to create a string from
1227  @returns MutableString - the MutableString representation of 'data'
1228  */
1229  public static MutableString valueOf(char[] data) {
1230    return new MutableString(data);
1231  } // valueOf()
1232
1233  /**
1234  Creates a MutableString from the string representation of
1235  'data[offset..offset + count - 1]'.
1236
1237  @param char[] - the object to create a string from
1238  @param int - the offset into 'data' to begin copying from
1239  @param int - the amount of characters to extract from 'data'
1240  @returns MutableString - the MutableString representation of 'data'
1241  @throws NullPointerException - if data is null.
1242  @throws IndexOutOfBoundsException - if offset is negative, or count is
1243  negative, or offset+count is larger than data.length.
1244  */
1245  public static MutableString valueOf(char[] data,
1246                                      int offset,
1247                                      int count) {
1248    if (offset < 0 || count < 0 || offset + count >= data.length) {
1249      throw new IndexOutOfBoundsException();
1250    } // if
1251    
1252    char[] destination = new char[count];
1253    System.arraycopy(data, offset, destination, 0, count);
1254    return new MutableString(destination);
1255  } // valueOf()
1256
1257  /**
1258  Creates a MutableString from the string representation of
1259  'data[offset..offset + count - 1]'.
1260
1261  @param char[] - the object to create a string from
1262  @param int - the offset into 'data' to begin copying from
1263  @param int - the amount of characters to extract from 'data'
1264  @returns MutableString - the MutableString representation of 'data'
1265  @throws NullPointerException - if data is null.
1266  @throws IndexOutOfBoundsException - if offset is negative, or count is
1267  negative, or offset+count is larger than data.length.
1268  */
1269  public static MutableString copyValueOf(char[] data,
1270                                          int offset,
1271                                          int count) {
1272    // how is this different than valueOf(char[], int, int)?
1273    return valueOf(data, offset, count);
1274  } // copyValueOf()
1275
1276  /**
1277  Creates a MutableString from the string representation of 'data'
1278
1279  @param char[] - the object to create a string from
1280  @returns MutableString - the MutableString representation of 'data'
1281  @throws NullPointerException - if data is null.
1282  @throws IndexOutOfBoundsException - if offset is negative, or count is
1283  negative, or offset+count is larger than data.length.
1284  */
1285  public static MutableString copyValueOf(char[] data) {
1286    return valueOf(data, 0, data.length);
1287  } // copyValueOf()
1288
1289  /**
1290  Creates a MutableString from the string representation of 'b'.
1291
1292  @param char[] - the object to create a string from
1293  @returns MutableString - the MutableString representation of 'b'
1294  */
1295  public static MutableString valueOf(boolean b) {
1296    if (b) {
1297      return new MutableString("true");
1298    } // if
1299    else {
1300      return new MutableString("false");
1301    } // else
1302  } // valueOf()
1303
1304  /**
1305  Creates a MutableString from the string representation of 'c'.
1306
1307  @param char[] - the object to create a string from
1308  @returns MutableString - the MutableString representation of 'c'
1309  */
1310  public static MutableString valueOf(char c) {
1311    return new MutableString((new Character(c)).toString());
1312  } // valueOf()
1313
1314  /**
1315  Creates a MutableString from the string representation of 'i'.
1316
1317  @param char[] - the object to create a string from
1318  @returns MutableString - the MutableString representation of 'i'
1319  */
1320  public static MutableString valueOf(int i) {
1321    return new MutableString(Integer.toString(i));
1322  } // valueOf()
1323
1324  /**
1325  Creates a MutableString from the string representation of 'l'.
1326
1327  @param char[] - the object to create a string from
1328  @returns MutableString - the MutableString representation of 'l'
1329  */
1330  public static MutableString valueOf(long l) {
1331    return new MutableString(Long.toString(l));
1332  } // valueOf()
1333
1334  /**
1335  Creates a MutableString from the string representation of 'f'.
1336
1337  @param char[] - the object to create a string from
1338  @returns MutableString - the MutableString representation of 'f'
1339  */
1340  public static MutableString valueOf(float f) {
1341    return new MutableString(Float.toString(f));
1342  } // valueOf()
1343
1344  /**
1345  Creates a MutableString from the string representation of 'd'.
1346
1347  @param char[] - the object to create a string from
1348  @returns MutableString - the MutableString representation of 'd'
1349  */
1350  public static MutableString valueOf(double d) {
1351    return new MutableString(Double.toString(d));
1352  } // valueOf()
1353
1354  /***********************/
1355  /* end of String class */
1356  /***********************/
1357
1358  /**********************/
1359  /* StringBuffer class */
1360  /**********************/
1361
1362  /**
1363  Makes sure the capacity of the object is at least 'minimumCapacity'.
1364
1365  @param int - the minimum capacity
1366  */
1367  public void ensureCapacity(int minimumCapacity) {
1368    stringArray.ensureCapacity(minimumCapacity);
1369  } // ensureCapacity()
1370
1371  /**
1372  Resets the string to string[0..newLength - 1]. If the old string is
1373  shorter, characters are truncated. If the old string is longer, null
1374  characters are inserted.
1375
1376  @param int - the new length of the string
1377  @throws IndexOutOfBoundsException - if 'newLength' is negative
1378  */
1379  public void setLength(int newLength) {
1380    if (newLength < 0) {
1381      throw new IndexOutOfBoundsException();
1382    } // if
1383
1384    if (newLength >= stringArray.size()) {
1385      // string is becoming longer
1386
1387      // note: there's got to be a faster way to do this      
1388      for (int index = 0; index < newLength - stringArray.size() + 1; ++index) {
1389        stringArray.add(new Character('\u0000'));
1390      } // for index
1391    } // if
1392    else {
1393      // string is becoming shorter
1394
1395      // note: there's got to be a faster way to do this
1396      for (int index = stringArray.size() - 1; index >= newLength; --index) {
1397        stringArray.remove(index);
1398      } // for index
1399    } // else
1400  } // setLength()
1401
1402  /**
1403  Sets the character at offset 'index' to 'ch'.
1404
1405  @throws IndexOutOfBoundsException - if index is negative or greater than or
1406  equal to length()
1407  */
1408  public void setCharAt(int index,
1409                        char ch) {
1410    if (index < 0 || index >= stringArray.size()) {
1411      throw new IndexOutOfBoundsException();
1412    } // if
1413    
1414    stringArray.set(index, new Character(ch));
1415  } // setCharAt()
1416
1417  /**
1418  Appends the string representation of 'obj' to the object.
1419
1420  @return MutableString - the object after appending
1421  */
1422  public MutableString append(Object obj) {
1423    append(obj.toString());
1424
1425    return this;
1426  } // append()
1427
1428  /**
1429  Appends 'str' to the object.
1430
1431  @return MutableString - the object after appending
1432  */
1433  public MutableString append(MutableString str) {
1434    concat(str);
1435
1436    return this;
1437  } // append()
1438
1439  /**
1440  Appends 'str' to the object.
1441
1442  @return MutableString - the object after appending
1443  */
1444  public MutableString append(String str) {
1445    concat(str);
1446
1447    return this;
1448  } // append()
1449
1450  /**
1451  Appends 'str' to the object.
1452
1453  @return MutableString - the object after appending
1454  */
1455  public MutableString append(char[] str) {
1456    append(new String(str));
1457
1458    return this;
1459  } // append()
1460
1461  /**
1462  Appends 'str[offset]..str[len-1]' to the object.
1463
1464  @return MutableString - the object after appending
1465  */
1466  public MutableString append(char[] str,
1467                              int offset,
1468                              int len) {
1469    MutableString ms = MutableString.valueOf(str, offset, len);
1470    append(ms);
1