Add love
[mono.git] / mcs / class / corlib / System.Text / StringBuilder.cs
1 // -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-\r
2 //\r
3 // System.Text.StringBuilder\r
4 //\r
5 // Author: Marcin Szczepanski (marcins@zipworld.com.au)\r
6 //\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
10 // pan out first.\r
11 //  \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
14 //\r
15 namespace System.Text {\r
16         public sealed class StringBuilder {\r
17 \r
18                 private const int defaultCapacity = 16;\r
19 \r
20                 private int sCapacity;\r
21                 private int sLength;\r
22                 private char[] sString;\r
23                 private int sMaxCapacity = Int32.MaxValue;\r
24 \r
25                 public StringBuilder(string value, int startIndex, int length, int capacity) {\r
26                         // first, check the parameters and throw appropriate exceptions if needed\r
27                         if(null==value) {\r
28                                 throw new System.ArgumentNullException("value");\r
29                         }\r
30 \r
31                         // make sure startIndex is zero or positive\r
32                         if(startIndex < 0) {\r
33                                 throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex cannot be less than zero.");\r
34                         }\r
35 \r
36                         // make sure length is zero or positive\r
37                         if(length < 0) {\r
38                                 throw new System.ArgumentOutOfRangeException("length", length, "Length cannot be less than zero.");\r
39                         }\r
40 \r
41                         // make sure startIndex and length give a valid substring of value\r
42                         if(startIndex + (length -1) > (value.Length - 1) ) {\r
43                                 throw new System.ArgumentOutOfRangeException("startIndex", startIndex, "StartIndex and length must refer to a location within the string.");\r
44                         }\r
45                         \r
46                         // the capacity must be at least as big as the default capacity\r
47                         sCapacity = Math.Max(capacity, defaultCapacity);\r
48 \r
49                         // LAMESPEC: what to do if capacity is too small to hold the substring?\r
50                         // Like the MS implementation, double the capacity until it is large enough\r
51                         while (sCapacity < length) {\r
52                                 // However, take care not to double if that would make the number\r
53                                 // larger than what an int can hold\r
54                                 if (sCapacity <= Int32.MaxValue / 2) {\r
55                                         sCapacity *= 2;\r
56                                 }\r
57                                 else{\r
58                                         sCapacity = Int32.MaxValue;\r
59                                 }\r
60                         }\r
61 \r
62                         sString = new char[sCapacity];\r
63                         sLength = length;\r
64 \r
65                         // if the length is not zero, then we have to copy some characters\r
66                         if (sLength > 0) {\r
67                                 // Copy the correct number of characters into the internal array\r
68                                 char[] tString = value.ToCharArray(startIndex, sLength);\r
69                                 Array.Copy( tString, sString, sLength);\r
70                         }\r
71                 }\r
72 \r
73                 public StringBuilder() : this(String.Empty, 0, 0, 0) {}\r
74 \r
75                 public StringBuilder( int capacity ) : this("", 0, 0, capacity) {}\r
76 \r
77                 public StringBuilder( int capacity, int maxCapacity ) : this("", 0, 0, capacity) {\r
78                         if(capacity > maxCapacity) {\r
79                                 throw new System.ArgumentOutOfRangeException("capacity", "Capacity exceeds maximum capacity.");\r
80                         }\r
81                         sMaxCapacity = maxCapacity;\r
82                 }\r
83 \r
84                 public StringBuilder( string value ) : this(value, 0, value == null ? 0 : value.Length, value == null? 0 : value.Length) {\r
85                 }\r
86         \r
87                 public StringBuilder( string value, int capacity) : this(value, 0, value.Length, capacity) {}\r
88         \r
89                 public int MaxCapacity {\r
90                         get {\r
91                                 // TODO: Need to look at the memory of the system to return a useful value here\r
92                                 return sMaxCapacity;\r
93                         }\r
94                 }\r
95 \r
96                 public int Capacity {\r
97                         get {\r
98                                 return sCapacity;\r
99                         }\r
100 \r
101                         set {\r
102                                 if( value < sLength ) {\r
103                                         throw new ArgumentException( "Capacity must be > length" );\r
104                                 } else {\r
105                                         char[] tString = new char[value];              \r
106                                         Array.Copy( sString, tString, sLength );\r
107                                         sString = tString;\r
108                                         sCapacity = sString.Length;\r
109                                 }\r
110                         }\r
111                 }\r
112 \r
113 \r
114                 public int Length {\r
115                         get {\r
116                                 return sLength;\r
117                         }\r
118 \r
119                         set {\r
120                                 if( value < 0 || value > MaxCapacity) {\r
121                                         throw new ArgumentOutOfRangeException();\r
122                                 } else {\r
123                                         if( value < sLength ) {\r
124                                                 // Truncate current string at value\r
125 \r
126                                                 // LAMESPEC:  The spec is unclear as to what to do\r
127                                                 // with the capacity when truncating the string.\r
128                                                 //\r
129                                                 // Don't change the capacity, as this is what\r
130                                                 // the MS implementation does.\r
131 \r
132                                                 sLength = value;\r
133                                         } else {\r
134                                                 // Expand the capacity to the new length and\r
135                                                 // pad the string with spaces.\r
136                                                 \r
137                                                 // LAMESPEC: The spec says to put the spaces on the\r
138                                                 // left of the string however the MS implementation\r
139                                                 // puts them on the right.  We'll do that for \r
140                                                 // compatibility (!)\r
141 \r
142                                                 char[] tString = new char[ value ];\r
143                                                 int padLength = value - sLength;\r
144                                                 \r
145                                                 string padding = new String( ' ', padLength );\r
146                                                 Array.Copy( sString, tString, sLength );\r
147                                                 Array.Copy( padding.ToCharArray(), 0, tString, sLength, padLength );\r
148                                                 sString = tString;\r
149                                                 sLength = sString.Length;\r
150                                                 sCapacity = value;\r
151                                         }\r
152                                 }\r
153                         }\r
154                 }\r
155 \r
156                 public char this[ int index ] {\r
157                         get {\r
158 \r
159                                 if( index >= sLength || index < 0 ) {\r
160                                         throw new IndexOutOfRangeException();\r
161                                 }\r
162                                 return sString[ index ];\r
163                         } \r
164 \r
165                         set {\r
166                                 if( index >= sLength || index < 0 ) {\r
167                                         throw new IndexOutOfRangeException();\r
168                                 }\r
169                                 sString[ index ] = value;\r
170                         }\r
171                 }\r
172 \r
173                 public override string ToString() {\r
174                         return ToString(0, sLength);\r
175                 }\r
176 \r
177                 public string ToString( int startIndex, int length ) {\r
178                         if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {\r
179                                 throw new ArgumentOutOfRangeException();\r
180                         }\r
181         \r
182                         return new String( sString, startIndex, length );\r
183                 }\r
184 \r
185                 public int EnsureCapacity( int capacity ) {\r
186                         if( capacity < 0 ) {\r
187                                 throw new ArgumentOutOfRangeException( \r
188                                         "Capacity must be greater than 0." );\r
189                         }\r
190 \r
191                         if( capacity <= sCapacity ) {\r
192                                 return sCapacity;\r
193                         } else {\r
194                                 Capacity = capacity;\r
195                                 return sCapacity;\r
196                         }\r
197                 }\r
198 \r
199                 public bool Equals( StringBuilder sb ) {\r
200                         if( this.ToString() == sb.ToString() ) {\r
201                                 return true;\r
202                         } else {\r
203                                 return false;\r
204                         }\r
205                 }\r
206 \r
207                 public StringBuilder Remove( int startIndex, int length ) {\r
208                         if( startIndex < 0 || length < 0 || startIndex + length > sLength ) {\r
209                                 throw new ArgumentOutOfRangeException();\r
210                         }\r
211 \r
212                         // Copy everything after the 'removed' part to the start \r
213                         // of the removed part and truncate the sLength\r
214 \r
215                         Array.Copy( sString, startIndex + length, sString, \r
216                                 startIndex, length );\r
217 \r
218                         sLength -= length;\r
219                         return this;\r
220                 }                              \r
221 \r
222                 public StringBuilder Replace( char oldChar, char newChar ) {\r
223                 \r
224                         return Replace( oldChar, newChar, 0, sLength);\r
225                 }\r
226 \r
227                 public StringBuilder Replace( char oldChar, char newChar, int startIndex, int count ) {\r
228                         if( startIndex + count > sLength || startIndex < 0 || count < 0 ) {\r
229                                 throw new ArgumentOutOfRangeException();\r
230                         }\r
231 \r
232                         for( int replaceIterate = startIndex; replaceIterate < startIndex + count; replaceIterate++ ) {\r
233                                 if( this[replaceIterate] == oldChar ) {\r
234                                         this[replaceIterate] = newChar;\r
235                                 }\r
236                         }\r
237 \r
238                         return this;\r
239                 }\r
240 \r
241                 public StringBuilder Replace( string oldValue, string newValue ) {\r
242                         return Replace( oldValue, newValue, 0, sLength );\r
243                 }\r
244 \r
245                 public StringBuilder Replace( string oldValue, string newValue, int startIndex, int count ) {\r
246                         string startString = this.ToString();\r
247                         StringBuilder newStringB = new StringBuilder();\r
248                         string newString;\r
249 \r
250                         if( oldValue == null ) { \r
251                                 throw new ArgumentNullException(\r
252                                         "The old value cannot be null.");\r
253                         }\r
254 \r
255                         if( startIndex < 0 || count < 0 || startIndex + count > sLength ) {\r
256                                 throw new ArgumentOutOfRangeException();\r
257                         }\r
258 \r
259                         if( oldValue.Length == 0 ) {\r
260                                 throw new ArgumentException(\r
261                                         "The old value cannot be zero length.");\r
262                         }\r
263 \r
264                         int nextIndex = startIndex; // Where to start the next search\r
265                         int lastIndex = nextIndex;  // Where the last search finished\r
266 \r
267                         while( nextIndex != -1 ) {\r
268                                 nextIndex = startString.IndexOf( oldValue, lastIndex);                            \r
269                                 if( nextIndex != -1 ) {\r
270                                         // The MS implementation won't replace a substring \r
271                                         // if that substring goes over the "count"\r
272                                         // boundary, so we'll make sure the behaviour \r
273                                         // here is the same.\r
274 \r
275                                         if( nextIndex + oldValue.Length <= startIndex + count ) {\r
276 \r
277                                                 // Add everything to the left of the old \r
278                                                 // string\r
279                                                 newStringB.Append( startString.Substring( lastIndex, nextIndex - lastIndex ) );\r
280         \r
281                                                 // Add the replacement string\r
282                                                 newStringB.Append( newValue );\r
283                                                 \r
284                                                 // Set the next start point to the \r
285                                                 // end of the last match\r
286                                                 lastIndex = nextIndex + oldValue.Length;\r
287                                         } else {\r
288                                                 // We're past the "count" we're supposed to replace within\r
289                                                 nextIndex = -1;\r
290                                                 newStringB.Append( \r
291                                                         startString.Substring( lastIndex ) );\r
292                                         }\r
293 \r
294                                 } else {\r
295                                         // Append everything left over\r
296                                         newStringB.Append( startString.Substring( lastIndex ) );\r
297                                 }\r
298                         } \r
299 \r
300                         newString = newStringB.ToString();\r
301 \r
302                         EnsureCapacity( newString.Length );\r
303                         sString = newString.ToCharArray();\r
304                         sLength = newString.Length;\r
305                         return this;\r
306                 }\r
307 \r
308                       \r
309                 /* The Append Methods */\r
310 \r
311                 // TODO: Currently most of these methods convert the \r
312                 // parameter to a CharArray (via a String) and then pass\r
313                 // it to Append( char[] ).  There might be a faster way\r
314                 // of doing this, but it's probably adequate and anything else\r
315                 // would make it too messy.\r
316                 //\r
317                 // As an example, a sample test run of appending a 100 character\r
318                 // string to the StringBuilder, and loooping this 50,000 times\r
319                 // results in an elapsed time of 2.4s using the MS StringBuilder\r
320                 // and 2.7s using this StringBuilder.  Note that this results\r
321                 // in a 5 million character string.  I believe MS uses a lot\r
322                 // of "native" DLLs for the "meat" of the base classes.\r
323 \r
324 \r
325                 public StringBuilder Append( char[] value ) {\r
326                         if( sLength + value.Length > sCapacity ) {\r
327                                 // Need more capacity, double the capacity StringBuilder \r
328                                 // and make sure we have at least enough for the value \r
329                                 // if that's going to go over double. \r
330                                          \r
331                                 Capacity = value.Length + ( sCapacity + sCapacity);\r
332                         }\r
333 \r
334                         Array.Copy( value, 0, sString, sLength, value.Length );\r
335                         sLength += value.Length;\r
336 \r
337                         return this;\r
338                 } \r
339                 \r
340                 public StringBuilder Append( string value ) {\r
341                         if( value != null ) {\r
342                                 return Append( value.ToCharArray() );\r
343                         } else {\r
344                                 return null;\r
345                         }\r
346                 }\r
347 \r
348                 public StringBuilder Append( bool value ) {\r
349                         return Append( value.ToString().ToCharArray() );\r
350                 }\r
351                 \r
352                 public StringBuilder Append( byte value ) {\r
353                         return Append( value.ToString().ToCharArray() );\r
354                 }\r
355 \r
356                 public StringBuilder Append( int index, char value) {\r
357                         char[] appendChar = new char[1];\r
358                         \r
359                         appendChar[0] = value;\r
360                         return Append( appendChar );\r
361                 }\r
362 \r
363 \r
364                 public StringBuilder Append( decimal value ) {\r
365                         return Append( value.ToString().ToCharArray() );\r
366                 }\r
367 \r
368                 public StringBuilder Append( double value ) {\r
369                         return Append( value.ToString().ToCharArray() );\r
370                 }\r
371 \r
372                 public StringBuilder Append( short value ) {\r
373                         return Append( value.ToString().ToCharArray() );\r
374                 }\r
375 \r
376                 public StringBuilder Append( int value ) {\r
377                         return Append( value.ToString().ToCharArray() );\r
378                 }\r
379 \r
380                 public StringBuilder Append( long value ) {\r
381                         return Append( value.ToString().ToCharArray() );\r
382                 }\r
383 \r
384                 public StringBuilder Append( object value ) {\r
385                         return Append( value.ToString().ToCharArray() );\r
386                 }\r
387 \r
388                 [CLSCompliant(false)]\r
389                 public StringBuilder Append( sbyte value ) {\r
390                         return Append( value.ToString().ToCharArray() );\r
391                 }\r
392 \r
393                 public StringBuilder Append( float value ) {\r
394                         return Append( value.ToString().ToCharArray() );\r
395                 }\r
396 \r
397                 [CLSCompliant(false)]\r
398                 public StringBuilder Append( ushort value ) {\r
399                         return Append( value.ToString().ToCharArray() );\r
400                 }       \r
401                 \r
402                 [CLSCompliant(false)]\r
403                 public StringBuilder Append( uint value ) {\r
404                         return Append( value.ToString().ToCharArray() );\r
405                 }\r
406 \r
407                 [CLSCompliant(false)]\r
408                 public StringBuilder Append( ulong value ) {\r
409                         return Append( value.ToString().ToCharArray() );\r
410                 }\r
411 \r
412                 public StringBuilder Append( char value ) {\r
413                         return Append (value, 1);\r
414                 }\r
415 \r
416                 public StringBuilder Append( char value, int repeatCount ) {\r
417                         if( repeatCount < 0 ) {\r
418                                 throw new ArgumentOutOfRangeException();\r
419                         }\r
420 \r
421                         return Append( new String( value, repeatCount) );\r
422                 }\r
423 \r
424                 public StringBuilder Append( char[] value, int startIndex, int charCount ) {\r
425 \r
426                         if( (charCount < 0 || startIndex < 0) || \r
427                                 ( charCount + startIndex > value.Length ) ) {\r
428                                 throw new ArgumentOutOfRangeException();\r
429                         }\r
430                         \r
431                         if( value == null ) {\r
432                                 if( !(startIndex == 0 && charCount == 0) ) {\r
433                                         throw new ArgumentNullException();\r
434                                 } else {\r
435                                         return this;\r
436                                 }\r
437                         } else {\r
438                                 char[] appendChars = new char[ charCount ];\r
439                         \r
440                                 Array.Copy( value, startIndex, appendChars, 0, charCount );\r
441                                 return Append( appendChars );\r
442                         }\r
443                 }\r
444 \r
445                 public StringBuilder Append( string value, int startIndex, int count ) {\r
446                         if( (count < 0 || startIndex < 0) || \r
447                                 ( startIndex + count > value.Length ) ) { \r
448                                 throw new ArgumentOutOfRangeException();\r
449                         }\r
450 \r
451                         return Append( value.Substring( startIndex, count ).ToCharArray() );\r
452                 }\r
453 \r
454                 public StringBuilder AppendFormat( string format, object arg0 ) {\r
455                         // TODO: Implement\r
456                         return this;\r
457                 }\r
458 \r
459                 public StringBuilder AppendFormat( string format, params object[] args ) {\r
460                         // TODO: Implement\r
461                         return this;\r
462                 }\r
463 \r
464                 public StringBuilder AppendFormat( IFormatProvider provider, string format,\r
465                         params object[] args ) {\r
466                         // TODO: Implement\r
467                         return this;\r
468                 }\r
469 \r
470                 public StringBuilder AppendFormat( string format, object arg0, object arg1 ) {\r
471                         // TODO: Implement;\r
472                         return this;\r
473                 }\r
474 \r
475                 public StringBuilder AppendFormat( string format, object arg0, object arg1, object arg2 ) {\r
476                         // TODO Implement\r
477                         return this;\r
478                 }\r
479 \r
480                 /*  The Insert Functions */\r
481                 \r
482                 // Similarly to the Append functions, get everything down to a CharArray \r
483                 // and insert that.\r
484                 \r
485                 public StringBuilder Insert( int index, char[] value ) {\r
486                         if( index > sLength || index < 0) {\r
487                                 throw new ArgumentOutOfRangeException();\r
488                         }\r
489 \r
490                         if( value == null || value.Length == 0 ) {\r
491                                 return this;\r
492                         } else {\r
493                                 // Check we have the capacity to insert this array\r
494                                 if( sCapacity < sLength + value.Length ) {\r
495                                         Capacity = value.Length + ( sCapacity + sCapacity );\r
496                                 }\r
497 \r
498                                 // Move everything to the right of the insert point across\r
499                                 Array.Copy( sString, index, sString, index + value.Length, sLength - index);\r
500                                 \r
501                                 // Copy in stuff from the insert buffer\r
502                                 Array.Copy( value, 0, sString, index, value.Length );\r
503                                 \r
504                                 sLength += value.Length;\r
505                                 return this;\r
506                         }\r
507                 }\r
508                                 \r
509                 public StringBuilder Insert( int index, string value ) {\r
510                         return Insert( index, value.ToCharArray() );\r
511                 }\r
512 \r
513                 public StringBuilder Insert( int index, bool value ) {\r
514                         return Insert( index, value.ToString().ToCharArray() );\r
515                 }\r
516                 \r
517                 public StringBuilder Insert( int index, byte value ) {\r
518                         return Insert( index, value.ToString().ToCharArray() );\r
519                 }\r
520 \r
521                 public StringBuilder Insert( int index, char value) {\r
522                         char[] insertChar = new char[1];\r
523                         \r
524                         insertChar[0] = value;\r
525                         return Insert( index, insertChar );\r
526                 }\r
527 \r
528                 public StringBuilder Insert( int index, decimal value ) {\r
529                         return Insert( index, value.ToString().ToCharArray() );\r
530                 }\r
531 \r
532                 public StringBuilder Insert( int index, double value ) {\r
533                         return Insert( index, value.ToString().ToCharArray() );\r
534                 }\r
535                 \r
536                 public StringBuilder Insert( int index, short value ) {\r
537                         return Insert( index, value.ToString().ToCharArray() );\r
538                 }\r
539 \r
540                 public StringBuilder Insert( int index, int value ) {\r
541                         return Insert( index, value.ToString().ToCharArray() );\r
542                 }\r
543 \r
544                 public StringBuilder Insert( int index, long value ) {\r
545                         return Insert( index, value.ToString().ToCharArray() );\r
546                 }\r
547         \r
548                 public StringBuilder Insert( int index, object value ) {\r
549                         return Insert( index, value.ToString().ToCharArray() );\r
550                 }\r
551                 \r
552                 [CLSCompliant(false)]\r
553                 public StringBuilder Insert( int index, sbyte value ) {\r
554                         return Insert( index, value.ToString().ToCharArray() );\r
555                 }\r
556 \r
557                 public StringBuilder Insert( int index, float value ) {\r
558                         return Insert( index, value.ToString().ToCharArray() );\r
559                 }\r
560 \r
561                 [CLSCompliant(false)]\r
562                 public StringBuilder Insert( int index, ushort value ) {\r
563                         return Insert( index, value.ToString().ToCharArray() );\r
564                 }\r
565 \r
566                 [CLSCompliant(false)]\r
567                 public StringBuilder Insert( int index, uint value ) {\r
568                         return Insert( index, value.ToString().ToCharArray() );\r
569                 }\r
570                 \r
571                 [CLSCompliant(false)]\r
572                 public StringBuilder Insert( int index, ulong value ) {\r
573                         return Insert( index, value.ToString().ToCharArray() );\r
574                 }\r
575 \r
576                 public StringBuilder Insert( int index, string value, int count ) {\r
577                         if ( count < 0 ) {\r
578                                 throw new ArgumentOutOfRangeException();\r
579                         }\r
580 \r
581                         if( value != null ) {\r
582                                 if( value != "" ) {\r
583                                         for( int insertCount = 0; insertCount < count; \r
584                                                 insertCount++ ) {\r
585                                                 Insert( index, value.ToCharArray() );      \r
586                                         }\r
587                                 }\r
588                         }\r
589                         return this;\r
590                 }\r
591 \r
592                 public StringBuilder Insert( int index, char[] value, int startIndex, \r
593                         int charCount ) {\r
594 \r
595                         if( value != null ) {\r
596                                 if( charCount < 0 || startIndex < 0 || startIndex + charCount > value.Length ) {\r
597                                         throw new ArgumentOutOfRangeException();\r
598                                 }\r
599                                         \r
600                                 char[] insertChars = new char[ charCount  ];\r
601                                 Array.Copy( value, startIndex, insertChars, 0, charCount );\r
602                                 return Insert( index, insertChars );\r
603                         } else {\r
604                                 return this;\r
605                         }\r
606                 }\r
607         }\r
608 }       \r