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 IFormatProvider / 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
15 namespace System.Text {
\r
17 [MonoTODO ("Implement AppendFormat methods and IFormatProvider, IFormattable")]
\r
18 public sealed class StringBuilder {
\r
20 private const int defaultCapacity = 16;
\r
22 private int sCapacity;
\r
23 private int sLength;
\r
24 private char[] sString;
\r
25 private int sMaxCapacity = Int32.MaxValue;
\r
27 public StringBuilder(string value, int startIndex, int length, int capacity) {
\r
28 // first, check the parameters and throw appropriate exceptions if needed
\r
30 throw new System.ArgumentNullException("value");
\r
33 // make sure startIndex is zero or positive
\r
34 if(startIndex < 0) {
\r
35 throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex cannot be less than zero.");
\r
38 // make sure length is zero or positive
\r
40 throw new System.ArgumentOutOfRangeException("length", length, "Length cannot be less than zero.");
\r
43 // make sure startIndex and length give a valid substring of value
\r
44 if(startIndex + (length -1) > (value.Length - 1) ) {
\r
45 throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");
\r
48 // the capacity must be at least as big as the default capacity
\r
49 sCapacity = Math.Max(capacity, defaultCapacity);
\r
51 // LAMESPEC: what to do if capacity is too small to hold the substring?
\r
52 // Like the MS implementation, double the capacity until it is large enough
\r
53 while (sCapacity < length) {
\r
54 // However, take care not to double if that would make the number
\r
55 // larger than what an int can hold
\r
56 if (sCapacity <= Int32.MaxValue / 2) {
\r
60 sCapacity = Int32.MaxValue;
\r
64 sString = new char[sCapacity];
\r
67 // if the length is not zero, then we have to copy some characters
\r
69 // Copy the correct number of characters into the internal array
\r
70 char[] tString = value.ToCharArray(startIndex, sLength);
\r
71 Array.Copy( tString, sString, sLength);
\r
75 public StringBuilder() : this(String.Empty, 0, 0, 0) {}
\r
77 public StringBuilder( int capacity ) : this("", 0, 0, capacity) {}
\r
79 public StringBuilder( int capacity, int maxCapacity ) : this("", 0, 0, capacity) {
\r
80 if(capacity > maxCapacity) {
\r
81 throw new System.ArgumentOutOfRangeException("capacity", "Capacity exceeds maximum capacity.");
\r
83 sMaxCapacity = maxCapacity;
\r
86 public StringBuilder( string value ) : this(value, 0, value == null ? 0 : value.Length, value == null? 0 : value.Length) {
\r
89 public StringBuilder( string value, int capacity) : this(value, 0, value.Length, capacity) {}
\r
92 public int MaxCapacity {
\r
94 // TODO: Need to look at the memory of the system to return a useful value here
\r
95 return sMaxCapacity;
\r
99 public int Capacity {
\r
105 if( value < sLength ) {
\r
106 throw new ArgumentException( "Capacity must be > length" );
\r
108 char[] tString = new char[value];
\r
109 Array.Copy( sString, tString, sLength );
\r
111 sCapacity = sString.Length;
\r
117 public int Length {
\r
123 if( value < 0 || value > MaxCapacity) {
\r
124 throw new ArgumentOutOfRangeException();
\r
126 if( value < sLength ) {
\r
127 // Truncate current string at value
\r
129 // LAMESPEC: The spec is unclear as to what to do
\r
130 // with the capacity when truncating the string.
\r
132 // Don't change the capacity, as this is what
\r
133 // the MS implementation does.
\r
137 // Expand the capacity to the new length and
\r
138 // pad the string with spaces.
\r
140 // LAMESPEC: The spec says to put the spaces on the
\r
141 // left of the string however the MS implementation
\r
142 // puts them on the right. We'll do that for
\r
143 // compatibility (!)
\r
145 char[] tString = new char[ value ];
\r
146 int padLength = value - sLength;
\r
148 string padding = new String( ' ', padLength );
\r
149 Array.Copy( sString, tString, sLength );
\r
150 Array.Copy( padding.ToCharArray(), 0, tString, sLength, padLength );
\r
152 sLength = sString.Length;
\r
159 public char this[ int index ] {
\r
162 if( index >= sLength || index < 0 ) {
\r
163 throw new IndexOutOfRangeException();
\r
165 return sString[ index ];
\r
169 if( index >= sLength || index < 0 ) {
\r
170 throw new IndexOutOfRangeException();
\r
172 sString[ index ] = value;
\r
176 public override string ToString() {
\r
177 return ToString(0, sLength);
\r
180 public string ToString( int startIndex, int length ) {
\r
181 if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {
\r
182 throw new ArgumentOutOfRangeException();
\r
185 return new String( sString, startIndex, length );
\r
188 public int EnsureCapacity( int capacity ) {
\r
189 if( capacity < 0 ) {
\r
190 throw new ArgumentOutOfRangeException(
\r
191 "Capacity must be greater than 0." );
\r
194 if( capacity <= sCapacity ) {
\r
197 Capacity = capacity;
\r
202 public bool Equals( StringBuilder sb ) {
\r
203 if( this.ToString() == sb.ToString() ) {
\r
210 public StringBuilder Remove( int startIndex, int length ) {
\r
211 if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {
\r
212 throw new ArgumentOutOfRangeException();
\r
215 // Copy everything after the 'removed' part to the start
\r
216 // of the removed part and truncate the sLength
\r
218 Array.Copy( sString, startIndex + length, sString,
\r
219 startIndex, length );
\r
225 public StringBuilder Replace( char oldChar, char newChar ) {
\r
227 return Replace( oldChar, newChar, 0, sLength);
\r
230 public StringBuilder Replace( char oldChar, char newChar, int startIndex, int count ) {
\r
231 if( startIndex + count > sLength || startIndex < 0 || count < 0 ) {
\r
232 throw new ArgumentOutOfRangeException();
\r
235 for( int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {
\r
236 if( this[replaceIterate] == oldChar ) {
\r
237 this[replaceIterate] = newChar;
\r
244 public StringBuilder Replace( string oldValue, string newValue ) {
\r
245 return Replace( oldValue, newValue, 0, sLength );
\r
248 public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count ) {
\r
249 string startString = this.ToString();
\r
250 StringBuilder newStringB = new StringBuilder();
\r
253 if( oldValue == null ) {
\r
254 throw new ArgumentNullException(
\r
255 "The old value cannot be null.");
\r
258 if( startIndex < 0 || count < 0 || startIndex + count > sLength ) {
\r
259 throw new ArgumentOutOfRangeException();
\r
262 if( oldValue.Length == 0 ) {
\r
263 throw new ArgumentException(
\r
264 "The old value cannot be zero length.");
\r
267 int nextIndex = startIndex; // Where to start the next search
\r
268 int lastIndex = nextIndex; // Where the last search finished
\r
270 while( nextIndex != -1 ) {
\r
271 nextIndex = startString.IndexOf( oldValue, lastIndex);
\r
272 if( nextIndex != -1 ) {
\r
273 // The MS implementation won't replace a substring
\r
274 // if that substring goes over the "count"
\r
275 // boundary, so we'll make sure the behaviour
\r
276 // here is the same.
\r
278 if( nextIndex + oldValue.Length <= startIndex + count ) {
\r
280 // Add everything to the left of the old
\r
282 newStringB.Append( startString.Substring( lastIndex, nextIndex - lastIndex ) );
\r
284 // Add the replacement string
\r
285 newStringB.Append( newValue );
\r
287 // Set the next start point to the
\r
288 // end of the last match
\r
289 lastIndex = nextIndex + oldValue.Length;
\r
291 // We're past the "count" we're supposed to replace within
\r
293 newStringB.Append(
\r
294 startString.Substring( lastIndex ) );
\r
298 // Append everything left over
\r
299 newStringB.Append( startString.Substring( lastIndex ) );
\r
303 newString = newStringB.ToString();
\r
305 EnsureCapacity( newString.Length );
\r
306 sString = newString.ToCharArray();
\r
307 sLength = newString.Length;
\r
312 /* The Append Methods */
\r
314 // TODO: Currently most of these methods convert the
\r
315 // parameter to a CharArray (via a String) and then pass
\r
316 // it to Append( char[] ). There might be a faster way
\r
317 // of doing this, but it's probably adequate and anything else
\r
318 // would make it too messy.
\r
320 // As an example, a sample test run of appending a 100 character
\r
321 // string to the StringBuilder, and loooping this 50,000 times
\r
322 // results in an elapsed time of 2.4s using the MS StringBuilder
\r
323 // and 2.7s using this StringBuilder. Note that this results
\r
324 // in a 5 million character string. I believe MS uses a lot
\r
325 // of "native" DLLs for the "meat" of the base classes.
\r
327 [MonoTODO ("Look at all Append methods and complete them if necessary")]
\r
328 public StringBuilder Append( char[] value ) {
\r
329 if( sLength + value.Length > sCapacity ) {
\r
330 // Need more capacity, double the capacity StringBuilder
\r
331 // and make sure we have at least enough for the value
\r
332 // if that's going to go over double.
\r
334 Capacity = value.Length + ( sCapacity + sCapacity);
\r
337 Array.Copy( value, 0, sString, sLength, value.Length );
\r
338 sLength += value.Length;
\r
343 public StringBuilder Append( string value ) {
\r
344 if( value != null ) {
\r
345 return Append( value.ToCharArray() );
\r
351 public StringBuilder Append( bool value ) {
\r
352 return Append( value.ToString().ToCharArray() );
\r
355 public StringBuilder Append( byte value ) {
\r
356 return Append( value.ToString().ToCharArray() );
\r
359 public StringBuilder Append( int index, char value) {
\r
360 char[] appendChar = new char[1];
\r
362 appendChar[0] = value;
\r
363 return Append( appendChar );
\r
367 public StringBuilder Append( decimal value ) {
\r
368 return Append( value.ToString().ToCharArray() );
\r
371 public StringBuilder Append( double value ) {
\r
372 return Append( value.ToString().ToCharArray() );
\r
375 public StringBuilder Append( short value ) {
\r
376 return Append( value.ToString().ToCharArray() );
\r
379 public StringBuilder Append( int value ) {
\r
380 return Append( value.ToString().ToCharArray() );
\r
383 public StringBuilder Append( long value ) {
\r
384 return Append( value.ToString().ToCharArray() );
\r
387 public StringBuilder Append( object value ) {
\r
388 return Append( value.ToString().ToCharArray() );
\r
391 [CLSCompliant(false)]
\r
392 public StringBuilder Append( sbyte value ) {
\r
393 return Append( value.ToString().ToCharArray() );
\r
396 public StringBuilder Append( float value ) {
\r
397 return Append( value.ToString().ToCharArray() );
\r
400 [CLSCompliant(false)]
\r
401 public StringBuilder Append( ushort value ) {
\r
402 return Append( value.ToString().ToCharArray() );
\r
405 [CLSCompliant(false)]
\r
406 public StringBuilder Append( uint value ) {
\r
407 return Append( value.ToString().ToCharArray() );
\r
410 [CLSCompliant(false)]
\r
411 public StringBuilder Append( ulong value ) {
\r
412 return Append( value.ToString().ToCharArray() );
\r
415 public StringBuilder Append( char value ) {
\r
416 return Append (value, 1);
\r
419 public StringBuilder Append( char value, int repeatCount ) {
\r
420 if( repeatCount < 0 ) {
\r
421 throw new ArgumentOutOfRangeException();
\r
424 return Append( new String( value, repeatCount) );
\r
427 public StringBuilder Append( char[] value, int startIndex, int charCount ) {
\r
429 if( (charCount < 0 || startIndex < 0) ||
\r
430 ( charCount + startIndex > value.Length ) ) {
\r
431 throw new ArgumentOutOfRangeException();
\r
434 if( value == null ) {
\r
435 if( !(startIndex == 0 && charCount == 0) ) {
\r
436 throw new ArgumentNullException();
\r
441 char[] appendChars = new char[ charCount ];
\r
443 Array.Copy( value, startIndex, appendChars, 0, charCount );
\r
444 return Append( appendChars );
\r
448 public StringBuilder Append( string value, int startIndex, int count ) {
\r
449 if( (count < 0 || startIndex < 0) ||
\r
450 ( startIndex + count > value.Length ) ) {
\r
451 throw new ArgumentOutOfRangeException();
\r
454 return Append( value.Substring( startIndex, count ).ToCharArray() );
\r
458 public StringBuilder AppendFormat( string format, object arg0 ) {
\r
464 public StringBuilder AppendFormat( string format, params object[] args ) {
\r
470 public StringBuilder AppendFormat( IFormatProvider provider, string format,
\r
471 params object[] args ) {
\r
477 public StringBuilder AppendFormat( string format, object arg0, object arg1 ) {
\r
478 // TODO: Implement;
\r
483 public StringBuilder AppendFormat( string format, object arg0, object arg1, object arg2 ) {
\r
488 /* The Insert Functions */
\r
490 // Similarly to the Append functions, get everything down to a CharArray
\r
491 // and insert that.
\r
493 public StringBuilder Insert( int index, char[] value ) {
\r
494 if( index > sLength || index < 0) {
\r
495 throw new ArgumentOutOfRangeException();
\r
498 if( value == null || value.Length == 0 ) {
\r
501 // Check we have the capacity to insert this array
\r
502 if( sCapacity < sLength + value.Length ) {
\r
503 Capacity = value.Length + ( sCapacity + sCapacity );
\r
506 // Move everything to the right of the insert point across
\r
507 Array.Copy( sString, index, sString, index + value.Length, sLength - index);
\r
509 // Copy in stuff from the insert buffer
\r
510 Array.Copy( value, 0, sString, index, value.Length );
\r
512 sLength += value.Length;
\r
517 public StringBuilder Insert( int index, string value ) {
\r
518 return Insert( index, value.ToCharArray() );
\r
521 public StringBuilder Insert( int index, bool value ) {
\r
522 return Insert( index, value.ToString().ToCharArray() );
\r
525 public StringBuilder Insert( int index, byte value ) {
\r
526 return Insert( index, value.ToString().ToCharArray() );
\r
529 public StringBuilder Insert( int index, char value) {
\r
530 char[] insertChar = new char[1];
\r
532 insertChar[0] = value;
\r
533 return Insert( index, insertChar );
\r
536 public StringBuilder Insert( int index, decimal value ) {
\r
537 return Insert( index, value.ToString().ToCharArray() );
\r
540 public StringBuilder Insert( int index, double value ) {
\r
541 return Insert( index, value.ToString().ToCharArray() );
\r
544 public StringBuilder Insert( int index, short value ) {
\r
545 return Insert( index, value.ToString().ToCharArray() );
\r
548 public StringBuilder Insert( int index, int value ) {
\r
549 return Insert( index, value.ToString().ToCharArray() );
\r
552 public StringBuilder Insert( int index, long value ) {
\r
553 return Insert( index, value.ToString().ToCharArray() );
\r
556 public StringBuilder Insert( int index, object value ) {
\r
557 return Insert( index, value.ToString().ToCharArray() );
\r
560 [CLSCompliant(false)]
\r
561 public StringBuilder Insert( int index, sbyte value ) {
\r
562 return Insert( index, value.ToString().ToCharArray() );
\r
565 public StringBuilder Insert( int index, float value ) {
\r
566 return Insert( index, value.ToString().ToCharArray() );
\r
569 [CLSCompliant(false)]
\r
570 public StringBuilder Insert( int index, ushort value ) {
\r
571 return Insert( index, value.ToString().ToCharArray() );
\r
574 [CLSCompliant(false)]
\r
575 public StringBuilder Insert( int index, uint value ) {
\r
576 return Insert( index, value.ToString().ToCharArray() );
\r
579 [CLSCompliant(false)]
\r
580 public StringBuilder Insert( int index, ulong value ) {
\r
581 return Insert( index, value.ToString().ToCharArray() );
\r
584 public StringBuilder Insert( int index, string value, int count ) {
\r
586 throw new ArgumentOutOfRangeException();
\r
589 if( value != null ) {
\r
590 if( value != "" ) {
\r
591 for( int insertCount = 0; insertCount < count;
\r
593 Insert( index, value.ToCharArray() );
\r
600 public StringBuilder Insert( int index, char[] value, int startIndex,
\r
603 if( value != null ) {
\r
604 if( charCount < 0 || startIndex < 0 || startIndex + charCount > value.Length ) {
\r
605 throw new ArgumentOutOfRangeException();
\r
608 char[] insertChars = new char[ charCount ];
\r
609 Array.Copy( value, startIndex, insertChars, 0, charCount );
\r
610 return Insert( index, insertChars );
\r