1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
\r
3 // System.Text.StringBuilder
\r
5 // Author: Marcin Szczepanski (marcins@zipworld.com.au)
\r
7 // TODO: Implement the AppendFormat methods. Wasn't sure how
\r
8 // best to do this at this early stage, might want to see
\r
9 // how the String class and the IFormatProvide / IFormattable interfaces
\r
12 // TODO: Make sure the coding complies to the ECMA draft, there's some
\r
13 // variable names that probably don't (like sString)
\r
16 namespace System.Text {
\r
17 public sealed class StringBuilder {
\r
19 const int defaultCapacity = 16;
\r
21 private int sCapacity;
\r
22 private int sLength;
\r
23 private char[] sString;
\r
25 public StringBuilder() {
\r
27 // The MS Implementation uses the default
\r
28 // capacity for a StringBuilder. The spec
\r
29 // says it's up to the implementer, but
\r
30 // we'll do it the MS way just in case.
\r
32 sString = new char[ defaultCapacity ];
\r
33 sCapacity = defaultCapacity;
\r
37 public StringBuilder( int capacity ) {
\r
38 if( capacity < defaultCapacity ) {
\r
39 // The spec says that the capacity
\r
40 // has to be at least the default capacity
\r
41 capacity = defaultCapacity;
\r
44 sString = new char[capacity];
\r
45 sCapacity = capacity;
\r
49 public StringBuilder( string str ) {
\r
51 if( str.Length < defaultCapacity ) {
\r
52 char[] tString = str.ToCharArray();
\r
53 sString = new char[ defaultCapacity ];
\r
54 Array.Copy( tString, sString, str.Length );
\r
55 sLength = str.Length;
\r
56 sCapacity = defaultCapacity;
\r
58 sString = str.ToCharArray();
\r
59 sCapacity = sString.Length;
\r
60 sLength = sString.Length;
\r
64 public int Capacity {
\r
70 if( value < sLength ) {
\r
71 throw new ArgumentException( "Capacity must be > length" );
\r
73 char[] tString = new char[value];
\r
74 Array.Copy( sString, tString, sLength );
\r
76 sCapacity = sString.Length;
\r
88 if( value < 0 || value > Int32.MaxValue) {
\r
89 throw new ArgumentOutOfRangeException();
\r
91 if( value < sLength ) {
\r
92 // Truncate current string at value
\r
94 // LAMESPEC: The spec is unclear as to what to do
\r
95 // with the capacity when truncating the string.
\r
97 // Don't change the capacity, as this is what
\r
98 // the MS implementation does.
\r
102 // Expand the capacity to the new length and
\r
103 // pad the string with spaces.
\r
105 // LAMESPEC: The spec says to put the spaces on the
\r
106 // left of the string however the MS implementation
\r
107 // puts them on the right. We'll do that for
\r
108 // compatibility (!)
\r
110 char[] tString = new char[ value ];
\r
111 int padLength = value - sLength;
\r
113 string padding = new String( ' ', padLength );
\r
114 Array.Copy( sString, tString, sLength );
\r
115 Array.Copy( padding.ToCharArray(), 0, tString, sLength, padLength );
\r
117 sLength = sString.Length;
\r
124 public char this[ int index ] {
\r
127 if( index >= sLength || index < 0 ) {
\r
128 throw new IndexOutOfRangeException();
\r
130 return sString[ index ];
\r
134 if( index >= sLength || index < 0 ) {
\r
135 throw new IndexOutOfRangeException();
\r
137 sString[ index ] = value;
\r
141 public override string ToString() {
\r
142 return ToString(0, sLength);
\r
145 public string ToString( int startIndex, int length ) {
\r
146 if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {
\r
147 throw new ArgumentOutOfRangeException();
\r
150 return new String( sString, startIndex, length );
\r
153 public int EnsureCapacity( int capacity ) {
\r
154 if( capacity < 0 ) {
\r
155 throw new ArgumentOutOfRangeException(
\r
156 "Capacity must be greater than 0." );
\r
159 if( capacity <= sCapacity ) {
\r
162 Capacity = capacity;
\r
167 public bool Equals( StringBuilder sb ) {
\r
168 if( this.ToString() == sb.ToString() ) {
\r
175 public StringBuilder Remove( int startIndex, int length ) {
\r
176 if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {
\r
177 throw new ArgumentOutOfRangeException();
\r
180 // Copy everything after the 'removed' part to the start
\r
181 // of the removed part and truncate the sLength
\r
183 Array.Copy( sString, startIndex + length, sString,
\r
184 startIndex, length );
\r
190 public StringBuilder Replace( char oldChar, char newChar ) {
\r
192 return Replace( oldChar, newChar, 0, sLength);
\r
195 public StringBuilder Replace( char oldChar, char newChar, int startIndex, int count ) {
\r
196 if( startIndex + count > sLength || startIndex < 0 || count < 0 ) {
\r
197 throw new ArgumentOutOfRangeException();
\r
200 for( int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
\r
201 if( this[replaceIterate] == oldChar ) {
\r
202 this[replaceIterate] = newChar;
\r
209 public StringBuilder Replace( string oldValue, string newValue ) {
\r
210 return Replace( oldValue, newValue, 0, sLength );
\r
213 public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count ) {
\r
214 string startString = this.ToString();
\r
215 StringBuilder newStringB = new StringBuilder();
\r
218 if( oldValue == null ) {
\r
219 throw new ArgumentNullException(
\r
220 "The old value cannot be null.");
\r
223 if( startIndex < 0 || count < 0 || startIndex + count > sLength ) {
\r
224 throw new ArgumentOutOfRangeException();
\r
227 if( oldValue.Length == 0 ) {
\r
228 throw new ArgumentException(
\r
229 "The old value cannot be zero length.");
\r
232 int nextIndex = startIndex; // Where to start the next search
\r
233 int lastIndex = nextIndex; // Where the last search finished
\r
235 while( nextIndex != -1 ) {
\r
236 nextIndex = startString.IndexOf( oldValue, lastIndex);
\r
237 if( nextIndex != -1 ) {
\r
238 // The MS implementation won't replace a substring
\r
239 // if that substring goes over the "count"
\r
240 // boundary, so we'll make sure the behaviour
\r
241 // here is the same.
\r
243 if( nextIndex + oldValue.Length <= startIndex + count ) {
\r
245 // Add everything to the left of the old
\r
247 newStringB.Append( startString.Substring( lastIndex, nextIndex - lastIndex ) );
\r
249 // Add the replacement string
\r
250 newStringB.Append( newValue );
\r
252 // Set the next start point to the
\r
253 // end of the last match
\r
254 lastIndex = nextIndex + oldValue.Length;
\r
256 // We're past the "count" we're supposed to replace within
\r
258 newStringB.Append(
\r
259 startString.Substring( lastIndex ) );
\r
263 // Append everything left over
\r
264 newStringB.Append( startString.Substring( lastIndex ) );
\r
268 newString = newStringB.ToString();
\r
270 EnsureCapacity( newString.Length );
\r
271 sString = newString.ToCharArray();
\r
272 sLength = newString.Length;
\r
277 /* The Append Methods */
\r
279 // TODO: Currently most of these methods convert the
\r
280 // parameter to a CharArray (via a String) and then pass
\r
281 // it to Append( char[] ). There might be a faster way
\r
282 // of doing this, but it's probably adequate and anything else
\r
283 // would make it too messy.
\r
285 // As an example, a sample test run of appending a 100 character
\r
286 // string to the StringBuilder, and loooping this 50,000 times
\r
287 // results in an elapsed time of 2.4s using the MS StringBuilder
\r
288 // and 2.7s using this StringBuilder. Note that this results
\r
289 // in a 5 million character string. I believe MS uses a lot
\r
290 // of "native" DLLs for the "meat" of the base classes.
\r
293 public StringBuilder Append( char[] value ) {
\r
294 if( sLength + value.Length > sCapacity ) {
\r
295 // Need more capacity, double the capacity StringBuilder
\r
296 // and make sure we have at least enough for the value
\r
297 // if that's going to go over double.
\r
299 Capacity = value.Length + ( sCapacity + sCapacity);
\r
302 Array.Copy( value, 0, sString, sLength, value.Length );
\r
303 sLength += value.Length;
\r
308 public StringBuilder Append( string value ) {
\r
309 if( value != null ) {
\r
310 return Append( value.ToCharArray() );
\r
316 public StringBuilder Append( bool value ) {
\r
317 return Append( value.ToString().ToCharArray() );
\r
320 public StringBuilder Append( byte value ) {
\r
321 return Append( value.ToString().ToCharArray() );
\r
324 public StringBuilder Append( int index, char value) {
\r
325 char[] appendChar = new char[1];
\r
327 appendChar[0] = value;
\r
328 return Append( appendChar );
\r
332 public StringBuilder Append( decimal value ) {
\r
333 return Append( value.ToString().ToCharArray() );
\r
336 public StringBuilder Append( double value ) {
\r
337 return Append( value.ToString().ToCharArray() );
\r
340 public StringBuilder Append( short value ) {
\r
341 return Append( value.ToString().ToCharArray() );
\r
344 public StringBuilder Append( int value ) {
\r
345 return Append( value.ToString().ToCharArray() );
\r
348 public StringBuilder Append( long value ) {
\r
349 return Append( value.ToString().ToCharArray() );
\r
352 public StringBuilder Append( object value ) {
\r
353 return Append( value.ToString().ToCharArray() );
\r
356 public StringBuilder Append( sbyte value ) {
\r
357 return Append( value.ToString().ToCharArray() );
\r
360 public StringBuilder Append( float value ) {
\r
361 return Append( value.ToString().ToCharArray() );
\r
364 public StringBuilder Append( ushort value ) {
\r
365 return Append( value.ToString().ToCharArray() );
\r
368 public StringBuilder Append( uint value ) {
\r
369 return Append( value.ToString().ToCharArray() );
\r
372 public StringBuilder Append( ulong value ) {
\r
373 return Append( value.ToString().ToCharArray() );
\r
376 public StringBuilder Append( char value, int repeatCount ) {
\r
377 if( repeatCount < 0 ) {
\r
378 throw new ArgumentOutOfRangeException();
\r
381 return Append( new String( value, repeatCount) );
\r
384 public StringBuilder Append( char[] value, int startIndex, int charCount ) {
\r
386 if( (charCount < 0 || startIndex < 0) ||
\r
387 ( charCount + startIndex > value.Length ) ) {
\r
388 throw new ArgumentOutOfRangeException();
\r
391 if( value == null ) {
\r
392 if( !(startIndex == 0 && charCount == 0) ) {
\r
393 throw new ArgumentNullException();
\r
398 char[] appendChars = new char[ charCount ];
\r
400 Array.Copy( value, startIndex, appendChars, 0, charCount );
\r
401 return Append( appendChars );
\r
405 public StringBuilder Append( string value, int startIndex, int count ) {
\r
406 if( (count < 0 || startIndex < 0) ||
\r
407 ( startIndex + count > value.Length ) ) {
\r
408 throw new ArgumentOutOfRangeException();
\r
411 return Append( value.Substring( startIndex, count ).ToCharArray() );
\r
414 public StringBuilder AppendFormat( string format, object arg0 ) {
\r
419 public StringBuilder AppendFormat( string format, params object[] args ) {
\r
424 public StringBuilder AppendFormat( IFormatProvider provider, string format,
\r
425 params object[] args ) {
\r
430 public StringBuilder AppendFormat( string format, object arg0, object arg1 ) {
\r
431 // TODO: Implement;
\r
435 public StringBuilder AppendFormat( string format, object arg0, object arg1, object arg2 ) {
\r
440 /* The Insert Functions */
\r
442 // Similarly to the Append functions, get everything down to a CharArray
\r
443 // and insert that.
\r
445 public StringBuilder Insert( int index, char[] value ) {
\r
446 if( index > sLength || index < 0) {
\r
447 throw new ArgumentOutOfRangeException();
\r
450 if( value == null || value.Length == 0 ) {
\r
453 // Check we have the capacity to insert this array
\r
454 if( sCapacity < sLength + value.Length ) {
\r
455 Capacity = value.Length + ( sCapacity + sCapacity );
\r
458 // Move everything to the right of the insert point across
\r
459 Array.Copy( sString, index, sString, index + value.Length, sLength - index);
\r
461 // Copy in stuff from the insert buffer
\r
462 Array.Copy( value, 0, sString, index, value.Length );
\r
464 sLength += value.Length;
\r
469 public StringBuilder Insert( int index, string value ) {
\r
470 return Insert( index, value.ToCharArray() );
\r
473 public StringBuilder Insert( int index, bool value ) {
\r
474 return Insert( index, value.ToString().ToCharArray() );
\r
477 public StringBuilder Insert( int index, byte value ) {
\r
478 return Insert( index, value.ToString().ToCharArray() );
\r
481 public StringBuilder Insert( int index, char value) {
\r
482 char[] insertChar = new char[1];
\r
484 insertChar[0] = value;
\r
485 return Insert( index, insertChar );
\r
488 public StringBuilder Insert( int index, decimal value ) {
\r
489 return Insert( index, value.ToString().ToCharArray() );
\r
492 public StringBuilder Insert( int index, double value ) {
\r
493 return Insert( index, value.ToString().ToCharArray() );
\r
496 public StringBuilder Insert( int index, short value ) {
\r
497 return Insert( index, value.ToString().ToCharArray() );
\r
500 public StringBuilder Insert( int index, int value ) {
\r
501 return Insert( index, value.ToString().ToCharArray() );
\r
504 public StringBuilder Insert( int index, long value ) {
\r
505 return Insert( index, value.ToString().ToCharArray() );
\r
508 public StringBuilder Insert( int index, object value ) {
\r
509 return Insert( index, value.ToString().ToCharArray() );
\r
512 public StringBuilder Insert( int index, sbyte value ) {
\r
513 return Insert( index, value.ToString().ToCharArray() );
\r
516 public StringBuilder Insert( int index, float value ) {
\r
517 return Insert( index, value.ToString().ToCharArray() );
\r
520 public StringBuilder Insert( int index, ushort value ) {
\r
521 return Insert( index, value.ToString().ToCharArray() );
\r
524 public StringBuilder Insert( int index, uint value ) {
\r
525 return Insert( index, value.ToString().ToCharArray() );
\r
528 public StringBuilder Insert( int index, ulong value ) {
\r
529 return Insert( index, value.ToString().ToCharArray() );
\r
532 public StringBuilder Insert( int index, string value, int count ) {
\r
534 throw new ArgumentOutOfRangeException();
\r
537 if( value != null ) {
\r
538 if( value != "" ) {
\r
539 for( int insertCount = 0; insertCount < count;
\r
541 Insert( index, value.ToCharArray() );
\r
548 public StringBuilder Insert( int index, char[] value, int startIndex,
\r
551 if( value != null ) {
\r
552 if( charCount < 0 || startIndex < 0 || startIndex + charCount > value.Length ) {
\r
553 throw new ArgumentOutOfRangeException();
\r
556 char[] insertChars = new char[ charCount ];
\r
557 Array.Copy( value, startIndex, insertChars, 0, charCount );
\r
558 return Insert( index, insertChars );
\r