PageRenderTime 90ms CodeModel.GetById 3ms app.highlight 74ms RepoModel.GetById 1ms app.codeStats 1ms

/IronPython_2_0/Src/IronPython/Runtime/Operations/StringOps.cs

#
C# | 2412 lines | 2117 code | 233 blank | 62 comment | 354 complexity | 7f5752d01ce870c21a534be4bfc5b52d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* ****************************************************************************
   2 *
   3 * Copyright (c) Microsoft Corporation. 
   4 *
   5 * This source code is subject to terms and conditions of the Microsoft Public License. A 
   6 * copy of the license can be found in the License.html file at the root of this distribution. If 
   7 * you cannot locate the  Microsoft Public License, please send an email to 
   8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
   9 * by the terms of the Microsoft Public License.
  10 *
  11 * You must not remove this notice, or any other, from this software.
  12 *
  13 *
  14 * ***************************************************************************/
  15
  16using System; using Microsoft;
  17using System.Collections;
  18using System.Collections.Generic;
  19using System.Diagnostics;
  20using System.Globalization;
  21using System.Reflection;
  22using System.Runtime.InteropServices;
  23using System.Text;
  24using IronPython.Runtime.Exceptions;
  25using IronPython.Runtime.Types;
  26using Microsoft.Scripting;
  27using Microsoft.Scripting.Math;
  28using Microsoft.Scripting.Runtime;
  29using Microsoft.Scripting.Utils;
  30using SpecialNameAttribute = System.Runtime.CompilerServices.SpecialNameAttribute; 
  31
  32namespace IronPython.Runtime.Operations {
  33    /// <summary>
  34    /// ExtensibleString is the base class that is used for types the user defines
  35    /// that derive from string.  It carries along with it the string's value and
  36    /// our converter recognizes it as a string.
  37    /// </summary>
  38    public class ExtensibleString : Extensible<string>, ICodeFormattable, IValueEquality, ISequence {
  39        public ExtensibleString() : base(String.Empty) { }
  40        public ExtensibleString(string self) : base(self) { }
  41
  42        public override string ToString() {
  43            return Value;
  44        }
  45
  46        #region ICodeFormattable Members
  47
  48        public virtual string/*!*/ __repr__(CodeContext/*!*/ context) {
  49            return StringOps.Quote(Value);
  50        }
  51
  52        #endregion        
  53
  54        [return: MaybeNotImplemented]
  55        public object __eq__(object other) {
  56            if (other is string || other is ExtensibleString)
  57                return RuntimeHelpers.BooleanToObject(((IValueEquality)this).ValueEquals(other));
  58
  59            return NotImplementedType.Value;
  60        }
  61
  62        [return: MaybeNotImplemented]
  63        public object __ne__(object other) {
  64            object res = __eq__(other);
  65            if (res != NotImplementedType.Value) return PythonOps.Not(res);
  66
  67            return res;
  68        }
  69
  70        #region IValueEquality members
  71
  72        int IValueEquality.GetValueHashCode() {
  73            return GetHashCode();
  74        }
  75
  76        bool IValueEquality.ValueEquals(object other) {
  77            if (other == null) return false;
  78
  79            ExtensibleString es = other as ExtensibleString;
  80            if (es != null) return Value == es.Value;
  81            string os = other as string;
  82            if (os != null) return Value == os;
  83
  84            return false;
  85        }
  86
  87        #endregion
  88
  89        #region ISequence Members
  90
  91        public virtual object this[int index] {
  92            get { return RuntimeHelpers.CharToString(Value[index]); }
  93        }
  94
  95        public object this[Slice slice] {
  96            get { return StringOps.GetItem(Value, slice); }
  97        }
  98
  99        public object __getslice__(int start, int stop) {
 100            return StringOps.__getslice__(Value, start, stop);
 101        }
 102
 103        #endregion
 104
 105        #region IPythonContainer Members
 106
 107        public virtual int __len__() {
 108            return Value.Length;
 109        }
 110
 111        public virtual bool __contains__(object value) {
 112            if (value is string) return Value.Contains((string)value);
 113            else if (value is ExtensibleString) return Value.Contains(((ExtensibleString)value).Value);
 114
 115            throw PythonOps.TypeErrorForBadInstance("expected string, got {0}", value);
 116        }
 117
 118        #endregion
 119
 120    }
 121
 122    /// <summary>
 123    /// StringOps is the static class that contains the methods defined on strings, i.e. 'abc'
 124    /// 
 125    /// Here we define all of the methods that a Python user would see when doing dir('abc').
 126    /// If the user is running in a CLS aware context they will also see all of the methods
 127    /// defined in the CLS System.String type.
 128    /// </summary>
 129    public static class StringOps {
 130        internal const int LowestUnicodeValue = 0x7f;
 131        private static readonly char[] Whitespace = new char[] { ' ', '\t', '\n', '\r', '\f' };
 132
 133        internal static object FastNew(CodeContext/*!*/ context, object x) {
 134            if (x == null) {
 135                return "None";
 136            }
 137            if (x is string) {
 138                // check ascii
 139                return CheckAsciiString(context, (string)x);
 140            }
 141
 142            // we don't invoke PythonOps.StringRepr here because we want to return the 
 143            // Extensible<string> directly back if that's what we received from __str__.
 144            object value = PythonContext.InvokeUnaryOperator(context, UnaryOperators.String, x);
 145            if (value is string || value is Extensible<string>) {
 146                return value;
 147            }
 148
 149            throw PythonOps.TypeError("expected str, got {0} from __str__", DynamicHelpers.GetPythonType(value).Name);
 150        }
 151
 152        private static object CheckAsciiString(CodeContext context, string s) {
 153            for (int i = 0; i < s.Length; i++) {
 154                if (s[i] > '\x80')
 155                    return StringOps.__new__(
 156                        context,
 157                        (PythonType)DynamicHelpers.GetPythonTypeFromType(typeof(String)),
 158                        s,
 159                        null,
 160                        "strict"
 161                        );
 162            }
 163            return s;
 164        }
 165
 166        #region Python Constructors
 167
 168        [StaticExtensionMethod]
 169        public static object __new__(CodeContext/*!*/ context, PythonType cls) {
 170            if (cls == TypeCache.String) {
 171                return "";
 172            } else {
 173                return cls.CreateInstance(context);
 174            }
 175        }
 176
 177        [StaticExtensionMethod]
 178        public static object __new__(CodeContext/*!*/ context, PythonType cls, object @object) {
 179            if (cls == TypeCache.String) {
 180                return FastNew(context, @object);
 181            } else {
 182                return cls.CreateInstance(context, @object);
 183            }
 184        }
 185
 186        [StaticExtensionMethod]
 187        public static object __new__(CodeContext/*!*/ context, PythonType cls, [NotNull]string @object) {
 188            if (cls == TypeCache.String) {
 189                return CheckAsciiString(context, @object);
 190            } else {
 191                return cls.CreateInstance(context, @object);
 192            }
 193        }
 194
 195        [StaticExtensionMethod]
 196        public static object __new__(CodeContext/*!*/ context, PythonType cls, [NotNull]ExtensibleString @object) {
 197            if (cls == TypeCache.String) {
 198                return FastNew(context, @object);
 199            } else {
 200                return cls.CreateInstance(context, @object);
 201            }
 202        }
 203
 204        [StaticExtensionMethod]
 205        public static object __new__(CodeContext/*!*/ context, PythonType cls, char @object) {
 206            if (cls == TypeCache.String) {
 207                return CheckAsciiString(context, RuntimeHelpers.CharToString(@object));
 208            } else {
 209                return cls.CreateInstance(context, @object);
 210            }
 211        }
 212
 213        [StaticExtensionMethod]
 214        public static object __new__(CodeContext/*!*/ context, PythonType cls, [NotNull]BigInteger @object) {
 215            if (cls == TypeCache.String) {
 216                return @object.ToString();
 217            } else {
 218                return cls.CreateInstance(context, @object);
 219            }
 220        }
 221
 222        [StaticExtensionMethod]
 223        public static object __new__(CodeContext/*!*/ context, PythonType cls, [NotNull]Extensible<BigInteger> @object) {
 224            if (cls == TypeCache.String) {
 225                return FastNew(context, @object);
 226            } else {
 227                return cls.CreateInstance(context, @object);
 228            }
 229        }
 230
 231        [StaticExtensionMethod]
 232        public static object __new__(CodeContext/*!*/ context, PythonType cls, int @object) {
 233            if (cls == TypeCache.String) {
 234                return @object.ToString();
 235            } else {
 236                return cls.CreateInstance(context, @object);
 237            }
 238        }
 239
 240        [StaticExtensionMethod]
 241        public static object __new__(CodeContext/*!*/ context, PythonType cls, bool @object) {
 242            if (cls == TypeCache.String) {
 243                return @object.ToString();
 244            } else {
 245                return cls.CreateInstance(context, @object);
 246            }
 247        }
 248
 249        [StaticExtensionMethod]
 250        public static object __new__(CodeContext/*!*/ context, PythonType cls, double @object) {
 251            if (cls == TypeCache.String) {
 252                return DoubleOps.__str__(context, @object);
 253            } else {
 254                return cls.CreateInstance(context, @object);
 255            }
 256        }
 257
 258        [StaticExtensionMethod]
 259        public static object __new__(CodeContext/*!*/ context, PythonType cls, Extensible<double> @object) {
 260            if (cls == TypeCache.String) {
 261                return FastNew(context, @object);
 262            } else {
 263                return cls.CreateInstance(context, @object);
 264            }
 265        }
 266
 267        [StaticExtensionMethod]
 268        public static object __new__(CodeContext/*!*/ context, PythonType cls, float @object) {
 269            if (cls == TypeCache.String) {
 270                return SingleOps.__str__(context, @object);
 271            } else {
 272                return cls.CreateInstance(context, @object);
 273            }
 274        }
 275
 276        [StaticExtensionMethod]
 277        public static object __new__(CodeContext/*!*/ context, PythonType cls,
 278            object @string,
 279            [DefaultParameterValue(null)] string encoding,
 280            [DefaultParameterValue("strict")] string errors) {
 281
 282            string str = @string as string;
 283            if (str == null) throw PythonOps.TypeError("converting to unicode: need string, got {0}", DynamicHelpers.GetPythonType(@string).Name);
 284
 285            if (cls == TypeCache.String) {
 286                return decode(context, str, encoding ?? PythonContext.GetContext(context).GetDefaultEncodingName(), errors);
 287            } else {
 288                return cls.CreateInstance(context, str, encoding, errors);
 289            }
 290        }
 291      
 292        #endregion
 293
 294        #region Python __ methods
 295
 296        public static bool __contains__(string s, string item) {
 297            return s.Contains(item);
 298        }
 299
 300        public static bool __contains__(string s, char item) {
 301            return s.IndexOf(item) != -1;
 302        }
 303
 304        public static int __len__(string s) {
 305            return s.Length;
 306        }
 307
 308        [SpecialName]
 309        public static string GetItem(string s, int index) {
 310            return RuntimeHelpers.CharToString(s[PythonOps.FixIndex(index, s.Length)]);
 311        }
 312
 313        [SpecialName]
 314        public static string GetItem(string s, object index) {
 315            return GetItem(s, Converter.ConvertToIndex(index));
 316        }
 317
 318        [SpecialName]
 319        public static string GetItem(string s, Slice slice) {
 320            if (slice == null) throw PythonOps.TypeError("string indicies must be slices or integers");
 321            int start, stop, step;
 322            slice.indices(s.Length, out start, out stop, out step);
 323            if (step == 1) {
 324                return stop > start ? s.Substring(start, stop - start) : String.Empty;
 325            } else {
 326                int index = 0;
 327                char[] newData;
 328                if (step > 0) {
 329                    if (start > stop) return String.Empty;
 330
 331                    int icnt = (stop - start + step - 1) / step;
 332                    newData = new char[icnt];
 333                    for (int i = start; i < stop; i += step) {
 334                        newData[index++] = s[i];
 335                    }
 336                } else {
 337                    if (start < stop) return String.Empty;
 338
 339                    int icnt = (stop - start + step + 1) / step;
 340                    newData = new char[icnt];
 341                    for (int i = start; i > stop; i += step) {
 342                        newData[index++] = s[i];
 343                    }
 344                }
 345                return new string(newData);
 346            }
 347        }
 348
 349        public static string __getslice__(string self, int x, int y) {
 350            Slice.FixSliceArguments(self.Length, ref x, ref y);
 351            if (x >= y) return String.Empty;
 352
 353            return self.Substring(x, y - x);
 354        }
 355
 356        
 357        #endregion
 358
 359        #region Public Python methods
 360
 361        public static string capitalize(this string self) {
 362            if (self.Length == 0) return self;
 363            return Char.ToUpper(self[0]) + self.Substring(1).ToLower();
 364        }
 365
 366        //  default fillchar (padding char) is a space
 367        public static string center(this string self, int width) {
 368            return center(self, width, ' ');
 369        }
 370
 371        public static string center(this string self, int width, char fillchar) {
 372            int spaces = width - self.Length;
 373            if (spaces <= 0) return self;
 374
 375            StringBuilder ret = new StringBuilder(width);
 376            ret.Append(fillchar, spaces / 2);
 377            ret.Append(self);
 378            ret.Append(fillchar, (spaces + 1) / 2);
 379            return ret.ToString();
 380        }
 381
 382        public static int count(this string self, string sub) {
 383            return count(self, sub, 0, self.Length);
 384        }
 385
 386        public static int count(this string self, string sub, int start) {
 387            return count(self, sub, start, self.Length);
 388        }
 389
 390        public static int count(this string self, string ssub, int start, int end) {
 391            if (ssub == null) throw PythonOps.TypeError("expected string for 'sub' argument, got NoneType");
 392            string v = self;
 393
 394            if (start > self.Length) {
 395                return 0;
 396            }
 397
 398            start = PythonOps.FixSliceIndex(start, self.Length);
 399            end = PythonOps.FixSliceIndex(end, self.Length);
 400
 401            if (ssub.Length == 0) {
 402                return Math.Max((end - start) + 1, 0);
 403            }
 404
 405            int count = 0;
 406            while (true) {
 407                if (end <= start) break;
 408                int index = v.IndexOf(ssub, start, end - start);
 409                if (index == -1) break;
 410                count++;
 411                start = index + ssub.Length;
 412            }
 413            return count;
 414        }
 415
 416        public static string decode(CodeContext/*!*/ context, string s) {
 417            return decode(context, s, Missing.Value, "strict");
 418        }
 419
 420        public static string decode(CodeContext/*!*/ context, string s, [Optional]object encoding, [DefaultParameterValue("strict")]string errors) {
 421            return RawDecode(context, s, encoding, errors);
 422        }
 423
 424        public static string encode(CodeContext/*!*/ context, string s, [Optional]object encoding, [DefaultParameterValue("strict")]string errors) {
 425            return RawEncode(context, s, encoding, errors);
 426        }
 427
 428        private static string CastString(object o) {
 429            string res = o as string;
 430            if (res != null) {
 431                return res;
 432            }
 433
 434            return ((Extensible<string>)o).Value;
 435        }
 436
 437        private static string AsString(object o) {
 438            string res = o as string;
 439            if (res != null) {
 440                return res;
 441            }
 442
 443            Extensible<string> es = o as Extensible<string>;
 444            if (es != null) {
 445                return es.Value;
 446            }
 447
 448            return null;
 449        }
 450        
 451        public static bool endswith(this string self, object suffix) {
 452            TryStringOrTuple(suffix);
 453            if (suffix is PythonTuple)
 454                return endswith(self, (PythonTuple)suffix);
 455            else
 456                return endswith(self, CastString(suffix));
 457        }
 458
 459        public static bool endswith(this string self, object suffix, int start) {
 460            TryStringOrTuple(suffix);
 461            if (suffix is PythonTuple)
 462                return endswith(self, (PythonTuple)suffix, start);
 463            else
 464                return endswith(self, CastString(suffix), start);
 465        }
 466
 467        public static bool endswith(this string self, object suffix, int start, int end) {
 468            TryStringOrTuple(suffix);
 469            if (suffix is PythonTuple)
 470                return endswith(self, (PythonTuple)suffix, start, end);
 471            else
 472                return endswith(self, CastString(suffix), start, end);
 473        }
 474
 475        public static string expandtabs(string self) {
 476            return expandtabs(self, 8);
 477        }
 478
 479        public static string expandtabs(this string self, int tabsize) {
 480            StringBuilder ret = new StringBuilder(self.Length * 2);
 481            string v = self;
 482            int col = 0;
 483            for (int i = 0; i < v.Length; i++) {
 484                char ch = v[i];
 485                switch (ch) {
 486                    case '\n':
 487                    case '\r': col = 0; ret.Append(ch); break;
 488                    case '\t':
 489                        if (tabsize > 0) {
 490                            int tabs = tabsize - (col % tabsize);
 491                            ret.Append(' ', tabs);
 492                            col = 0;
 493                        }
 494                        break;
 495                    default:
 496                        col++;
 497                        ret.Append(ch);
 498                        break;
 499                }
 500            }
 501            return ret.ToString();
 502        }
 503
 504        public static int find(this string self, string sub) {
 505            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 506            if (sub.Length == 1) return self.IndexOf(sub[0]);
 507            CompareInfo c = CultureInfo.InvariantCulture.CompareInfo;
 508            return c.IndexOf(self, sub, CompareOptions.Ordinal);
 509
 510        }
 511
 512        public static int find(this string self, string sub, int start) {
 513            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 514            if (start > self.Length) return -1;
 515            start = PythonOps.FixSliceIndex(start, self.Length);
 516            return self.IndexOf(sub, start);
 517        }
 518
 519        public static int find(this string self, string sub, int start, int end) {
 520            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 521            if (start > self.Length) return -1;
 522            start = PythonOps.FixSliceIndex(start, self.Length);
 523            end = PythonOps.FixSliceIndex(end, self.Length);
 524            if (end < start) return -1;
 525
 526            return self.IndexOf(sub, start, end - start);
 527        }
 528
 529        public static int index(this string self, string sub) {
 530            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 531            return index(self, sub, 0, self.Length);
 532        }
 533
 534        public static int index(this string self, string sub, int start) {
 535            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 536            return index(self, sub, start, self.Length);
 537        }
 538
 539        public static int index(this string self, string sub, int start, int end) {
 540            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 541            int ret = find(self, sub, start, end);
 542            if (ret == -1) throw PythonOps.ValueError("substring {0} not found in {1}", sub, self);
 543            return ret;
 544        }
 545
 546        public static bool isalnum(this string self) {
 547            if (self.Length == 0) return false;
 548            string v = self;
 549            for (int i = v.Length - 1; i >= 0; i--) {
 550                if (!Char.IsLetterOrDigit(v, i)) return false;
 551            }
 552            return true;
 553        }
 554
 555        public static bool isalpha(this string self) {
 556            if (self.Length == 0) return false;
 557            string v = self;
 558            for (int i = v.Length - 1; i >= 0; i--) {
 559                if (!Char.IsLetter(v, i)) return false;
 560            }
 561            return true;
 562        }
 563
 564        public static bool isdigit(this string self) {
 565            if (self.Length == 0) return false;
 566            string v = self;
 567            for (int i = v.Length - 1; i >= 0; i--) {
 568                if (!Char.IsDigit(v, i)) return false;
 569            }
 570            return true;
 571        }
 572
 573        public static bool isspace(this string self) {
 574            if (self.Length == 0) return false;
 575            string v = self;
 576            for (int i = v.Length - 1; i >= 0; i--) {
 577                if (!Char.IsWhiteSpace(v, i)) return false;
 578            }
 579            return true;
 580        }
 581
 582        public static bool isdecimal(this string self) {
 583            return isnumeric(self);
 584        }
 585
 586        public static bool isnumeric(this string self) {
 587            if (String.IsNullOrEmpty(self)) return false;
 588
 589            foreach (char c in self) {
 590                if (!Char.IsDigit(c)) return false;
 591            }
 592            return true;
 593        }
 594
 595        public static bool islower(this string self) {
 596            if (self.Length == 0) return false;
 597            string v = self;
 598            bool hasLower = false;
 599            for (int i = v.Length - 1; i >= 0; i--) {
 600                if (!hasLower && Char.IsLower(v, i)) hasLower = true;
 601                if (Char.IsUpper(v, i)) return false;
 602            }
 603            return hasLower;
 604        }
 605
 606        public static bool isupper(this string self) {
 607            if (self.Length == 0) return false;
 608            string v = self;
 609            bool hasUpper = false;
 610            for (int i = v.Length - 1; i >= 0; i--) {
 611                if (!hasUpper && Char.IsUpper(v, i)) hasUpper = true;
 612                if (Char.IsLower(v, i)) return false;
 613            }
 614            return hasUpper;
 615        }
 616
 617        //  return true if self is a titlecased string and there is at least one
 618        //  character in self; also, uppercase characters may only follow uncased
 619        //  characters (e.g. whitespace) and lowercase characters only cased ones. 
 620        //  return false otherwise.
 621        public static bool istitle(this string self) {
 622            if (self == null || self.Length == 0) return false;
 623
 624            string v = self;
 625            bool prevCharCased = false, currCharCased = false, containsUpper = false;
 626            for (int i = 0; i < v.Length; i++) {
 627                if (Char.IsUpper(v, i) || Char.GetUnicodeCategory(v, i) == UnicodeCategory.TitlecaseLetter) {
 628                    containsUpper = true;
 629                    if (prevCharCased)
 630                        return false;
 631                    else
 632                        currCharCased = true;
 633                } else if (Char.IsLower(v, i))
 634                    if (!prevCharCased)
 635                        return false;
 636                    else
 637                        currCharCased = true;
 638                else
 639                    currCharCased = false;
 640                prevCharCased = currCharCased;
 641            }
 642
 643            //  if we've gone through the whole string and haven't encountered any rule 
 644            //  violations but also haven't seen an Uppercased char, then this is not a 
 645            //  title e.g. '\n', all whitespace etc.
 646            return containsUpper;
 647        }
 648
 649        public static bool isunicode(this string self) {
 650            foreach (char c in self) {
 651                if (c >= LowestUnicodeValue) return true;
 652            }
 653            return false;
 654        }
 655
 656        //  Return a string which is the concatenation of the strings 
 657        //  in the sequence seq. The separator between elements is the 
 658        //  string providing this method
 659        public static string join(this string self, object sequence) {
 660            IEnumerator seq = PythonOps.GetEnumerator(sequence);
 661            if (!seq.MoveNext()) return "";
 662
 663            // check if we have just a sequnce of just one value - if so just
 664            // return that value.
 665            object curVal = seq.Current;
 666            if (!seq.MoveNext()) return Converter.ConvertToString(curVal);
 667
 668            StringBuilder ret = new StringBuilder();
 669            AppendJoin(curVal, 0, ret);
 670
 671            int index = 1;
 672            do {
 673                ret.Append(self);
 674
 675                AppendJoin(seq.Current, index, ret);
 676
 677                index++;
 678            } while (seq.MoveNext());
 679
 680            return ret.ToString();
 681        }
 682
 683        public static string join(this string/*!*/ self, [NotNull]List/*!*/ sequence) {
 684            if (sequence.__len__() == 0) return String.Empty;
 685
 686            lock (sequence) {
 687                if (sequence.__len__() == 1) {
 688                    return Converter.ConvertToString(sequence[0]);
 689                }
 690
 691                StringBuilder ret = new StringBuilder();
 692
 693                AppendJoin(sequence._data[0], 0, ret);
 694                for (int i = 1; i < sequence._size; i++) {
 695                    ret.Append(self);
 696                    AppendJoin(sequence._data[i], i, ret);
 697                }
 698
 699                return ret.ToString();
 700            }
 701        }
 702
 703        public static string ljust(this string self, int width) {
 704            return ljust(self, width, ' ');
 705        }
 706
 707        public static string ljust(this string self, int width, char fillchar) {
 708            int spaces = width - self.Length;
 709            if (spaces <= 0) return self;
 710
 711            StringBuilder ret = new StringBuilder(width);
 712            ret.Append(self);
 713            ret.Append(fillchar, spaces);
 714            return ret.ToString();
 715        }
 716
 717        public static string lower(this string self) {
 718            return self.ToLower();
 719        }
 720
 721        public static string lstrip(this string self) {
 722            return self.TrimStart(Whitespace);
 723        }
 724
 725        public static string lstrip(this string self, string chars) {
 726            if (chars == null) return lstrip(self);
 727            return self.TrimStart(chars.ToCharArray());
 728        }
 729
 730        public static PythonTuple partition(this string self, string sep) {
 731            if (sep == null)
 732                throw PythonOps.TypeError("expected string, got NoneType");
 733            if (sep.Length == 0)
 734                throw PythonOps.ValueError("empty separator");
 735
 736            object[] obj = new object[3] { "", "", "" };
 737
 738            if (self.Length != 0) {
 739                int index = find(self, sep);
 740                if (index == -1) {
 741                    obj[0] = self;
 742                } else {
 743                    obj[0] = self.Substring(0, index);
 744                    obj[1] = sep;
 745                    obj[2] = self.Substring(index + sep.Length, self.Length - index - sep.Length);
 746                }
 747            }
 748            return new PythonTuple(obj);
 749        }
 750
 751        private static string StringOrBuffer(object input) {
 752            string result = (input as string);
 753            if (result != null) {
 754                return result;
 755            }
 756            PythonBuffer buffer = (input as PythonBuffer);
 757            if (buffer != null) {
 758                return buffer.ToString();
 759            }
 760            throw PythonOps.TypeError("expected a character buffer object");
 761        }
 762
 763        public static string replace(this string self, object old, object new_) {
 764            string oldString = StringOrBuffer(old);
 765            string newString = StringOrBuffer(new_);
 766            if (oldString.Length == 0) return ReplaceEmpty(self, newString, self.Length + 1);
 767            return self.Replace(oldString, newString);
 768        }
 769
 770        public static string replace(this string self, object old, object new_, int maxsplit) {
 771            if (maxsplit == -1) return replace(self, old, new_);
 772            string oldString = StringOrBuffer(old);
 773            string newString = StringOrBuffer(new_);
 774            if (oldString.Length == 0) return ReplaceEmpty(self, newString, maxsplit);
 775
 776            string v = self;
 777            StringBuilder ret = new StringBuilder(v.Length);
 778
 779            int index;
 780            int start = 0;
 781
 782            while (maxsplit > 0 && (index = v.IndexOf(oldString, start)) != -1) {
 783                ret.Append(v.Substring(start, index - start));
 784                ret.Append(newString);
 785                start = index + oldString.Length;
 786                maxsplit--;
 787            }
 788            ret.Append(v.Substring(start));
 789
 790            return ret.ToString();
 791        }
 792
 793        public static int rfind(this string self, string sub) {
 794            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 795            return rfind(self, sub, 0, self.Length);
 796        }
 797
 798        public static int rfind(this string self, string sub, int start) {
 799            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 800            if (start > self.Length) return -1;
 801            return rfind(self, sub, start, self.Length);
 802        }
 803
 804        public static int rfind(this string self, string sub, int start, int end) {
 805            if (sub == null) throw PythonOps.TypeError("expected string, got NoneType");
 806            if (start > self.Length) return -1;
 807
 808            start = PythonOps.FixSliceIndex(start, self.Length);
 809            end = PythonOps.FixSliceIndex(end, self.Length);
 810
 811            if (start > end) return -1;     // can't possibly match anything, not even an empty string
 812            if (sub.Length == 0) return end;    // match at the end
 813            if (end == 0) return -1;    // can't possibly find anything
 814
 815            return self.LastIndexOf(sub, end - 1, end - start);
 816        }
 817
 818        public static int rindex(this string self, string sub) {
 819            return rindex(self, sub, 0, self.Length);
 820        }
 821
 822        public static int rindex(this string self, string sub, int start) {
 823            return rindex(self, sub, start, self.Length);
 824        }
 825
 826        public static int rindex(this string self, string sub, int start, int end) {
 827            int ret = rfind(self, sub, start, end);
 828            if (ret == -1) throw PythonOps.ValueError("substring {0} not found in {1}", sub, self);
 829            return ret;
 830        }
 831
 832        public static string rjust(this string self, int width) {
 833            return rjust(self, width, ' ');
 834        }
 835
 836        public static string rjust(this string self, int width, char fillchar) {
 837            int spaces = width - self.Length;
 838            if (spaces <= 0) return self;
 839
 840            StringBuilder ret = new StringBuilder(width);
 841            ret.Append(fillchar, spaces);
 842            ret.Append(self);
 843            return ret.ToString();
 844        }
 845
 846        public static PythonTuple rpartition(this string self, string sep) {
 847            if (sep == null)
 848                throw PythonOps.TypeError("expected string, got NoneType");
 849            if (sep.Length == 0)
 850                throw PythonOps.ValueError("empty separator");
 851
 852            object[] obj = new object[3] { "", "", "" };
 853            if (self.Length != 0) {
 854                int index = rfind(self, sep);
 855                if (index == -1) {
 856                    obj[2] = self;
 857                } else {
 858                    obj[0] = self.Substring(0, index);
 859                    obj[1] = sep;
 860                    obj[2] = self.Substring(index + sep.Length, self.Length - index - sep.Length);
 861                }
 862            }
 863            return new PythonTuple(obj);
 864        }
 865
 866        //  when no maxsplit arg is given then just use split
 867        public static List rsplit(this string self) {
 868            return SplitInternal(self, (char[])null, -1);
 869        }
 870
 871        public static List rsplit(this string self, string sep) {
 872            return rsplit(self, sep, -1);
 873        }
 874
 875        public static List rsplit(this string self, string sep, int maxsplit) {
 876            //  rsplit works like split but needs to split from the right;
 877            //  reverse the original string (and the sep), split, reverse 
 878            //  the split list and finally reverse each element of the list
 879            string reversed = Reverse(self);
 880            if (sep != null) sep = Reverse(sep);
 881            List temp = null, ret = null;
 882            temp = split(reversed, sep, maxsplit);
 883            temp.reverse();
 884            int resultlen = temp.__len__();
 885            if (resultlen != 0) {
 886                ret = new List(resultlen);
 887                foreach (string s in temp)
 888                    ret.AddNoLock(Reverse(s));
 889            } else {
 890                ret = temp;
 891            }
 892            return ret;
 893        }
 894
 895        public static string rstrip(this string self) {
 896            return self.TrimEnd(Whitespace);
 897        }
 898
 899        public static string rstrip(this string self, string chars) {
 900            if (chars == null) return rstrip(self);
 901            return self.TrimEnd(chars.ToCharArray());
 902        }
 903
 904        public static List split(this string self) {
 905            return SplitInternal(self, (char[])null, -1);
 906        }
 907
 908        public static List split(this string self, string sep) {
 909            return split(self, sep, -1);
 910        }
 911
 912        public static List split(this string self, string sep, int maxsplit) {
 913            if (sep == null) {
 914                if (maxsplit == 0) {
 915                    // Corner case for CPython compatibility
 916                    List result = PythonOps.MakeEmptyList(1);
 917                    result.AddNoLock(self.TrimStart());
 918                    return result;
 919                        
 920                } else {
 921                    return SplitInternal(self, (char[])null, maxsplit);
 922                }
 923            }
 924
 925            if (sep.Length == 0) {
 926                throw PythonOps.ValueError("empty separator");
 927            } else if (sep.Length == 1) {
 928                return SplitInternal(self, new char[] { sep[0] }, maxsplit);
 929            } else {
 930                return SplitInternal(self, sep, maxsplit);
 931            }
 932        }
 933
 934        public static List splitlines(this string self) {
 935            return splitlines(self, false);
 936        }
 937
 938        public static List splitlines(this string self, bool keepends) {
 939            List ret = new List();
 940            int i, linestart;
 941            for (i = 0, linestart = 0; i < self.Length; i++) {
 942                if (self[i] == '\n' || self[i] == '\r' || self[i] == '\x2028') {
 943                    //  special case of "\r\n" as end of line marker
 944                    if (i < self.Length - 1 && self[i] == '\r' && self[i + 1] == '\n') {
 945                        if (keepends)
 946                            ret.AddNoLock(self.Substring(linestart, i - linestart + 2));
 947                        else
 948                            ret.AddNoLock(self.Substring(linestart, i - linestart));
 949                        linestart = i + 2;
 950                        i++;
 951                    } else { //'\r', '\n', or unicode new line as end of line marker
 952                        if (keepends)
 953                            ret.AddNoLock(self.Substring(linestart, i - linestart + 1));
 954                        else
 955                            ret.AddNoLock(self.Substring(linestart, i - linestart));
 956                        linestart = i + 1;
 957                    }
 958                }
 959            }
 960            //  the last line needs to be accounted for if it is not empty
 961            if (i - linestart != 0)
 962                ret.AddNoLock(self.Substring(linestart, i - linestart));
 963            return ret;
 964        }
 965
 966
 967        public static bool startswith(this string self, object prefix) {
 968            TryStringOrTuple(prefix);
 969            if (prefix is PythonTuple)
 970                return startswith(self, (PythonTuple)prefix);
 971            else
 972                return startswith(self, CastString(prefix));
 973
 974        }
 975
 976        public static bool startswith(this string self, object prefix, int start) {
 977            TryStringOrTuple(prefix);
 978            if (prefix is PythonTuple)
 979                return startswith(self, (PythonTuple)prefix, start);
 980            else
 981                return startswith(self, CastString(prefix), start);
 982        }
 983
 984        public static bool startswith(this string self, object prefix, int start, int end) {
 985            TryStringOrTuple(prefix);
 986            if (prefix is PythonTuple)
 987                return startswith(self, (PythonTuple)prefix, start, end);
 988            else
 989                return startswith(self, CastString(prefix), start, end);
 990        }
 991
 992        public static string strip(this string self) {
 993            return self.Trim();
 994        }
 995
 996        public static string strip(this string self, string chars) {
 997            if (chars == null) return strip(self);
 998            return self.Trim(chars.ToCharArray());
 999        }
1000
1001        public static string swapcase(this string self) {
1002            StringBuilder ret = new StringBuilder(self);
1003            for (int i = 0; i < ret.Length; i++) {
1004                char ch = ret[i];
1005                if (Char.IsUpper(ch)) ret[i] = Char.ToLower(ch);
1006                else if (Char.IsLower(ch)) ret[i] = Char.ToUpper(ch);
1007            }
1008            return ret.ToString();
1009        }
1010
1011        public static string title(this string self) {
1012            if (self == null || self.Length == 0) return self;
1013
1014            char[] retchars = self.ToCharArray();
1015            bool prevCharCased = false;
1016            bool currCharCased = false;
1017            int i = 0;
1018            do {
1019                if (Char.IsUpper(retchars[i]) || Char.IsLower(retchars[i])) {
1020                    if (!prevCharCased)
1021                        retchars[i] = Char.ToUpper(retchars[i]);
1022                    else
1023                        retchars[i] = Char.ToLower(retchars[i]);
1024                    currCharCased = true;
1025                } else {
1026                    currCharCased = false;
1027                }
1028                i++;
1029                prevCharCased = currCharCased;
1030            }
1031            while (i < retchars.Length);
1032            return new string(retchars);
1033        }
1034
1035        //translate on a unicode string differs from that on an ascii
1036        //for unicode, the table argument is actually a dictionary with
1037        //character ordinals as keys and the replacement strings as values
1038        public static string translate(this string self, PythonDictionary table) {
1039            if (table == null) throw PythonOps.TypeError("expected dictionary or string, got NoneType");
1040            if (self.Length == 0) return self;
1041            StringBuilder ret = new StringBuilder();
1042            for (int i = 0, idx = 0; i < self.Length; i++) {
1043                idx = (int)self[i];
1044                if (table.__contains__(idx))
1045                    ret.Append((string)table[idx]);
1046                else
1047                    ret.Append(self[i]);
1048            }
1049            return ret.ToString();
1050        }
1051
1052        public static string translate(this string self, string table) {
1053            return translate(self, table, (string)null);
1054        }
1055
1056        public static string translate(this string self, string table, string deletechars) {
1057            if (table == null) {
1058                throw PythonOps.TypeError("expected string, got NoneType");
1059            } else if (table.Length != 256) {
1060                throw PythonOps.ValueError("translation table must be 256 characters long");
1061            } else if (self.Length == 0) {
1062                return self;
1063            }
1064
1065            // List<char> is about 2/3rds as expensive as StringBuilder appending individual 
1066            // char's so we use that instead of a StringBuilder
1067            List<char> res = new List<char>();
1068            for (int i = 0; i < self.Length; i++) {
1069                if (deletechars == null || !deletechars.Contains(Char.ToString(self[i]))) {
1070                    int idx = (int)self[i];
1071                    if (idx >= 0 && idx < 256) {
1072                        res.Add(table[idx]);
1073                    }
1074                }
1075            }
1076            return new String(res.ToArray());
1077        }
1078
1079        public static string upper(this string self) {
1080            return self.ToUpper();
1081        }
1082
1083        public static string zfill(this string self, int width) {
1084            int spaces = width - self.Length;
1085            if (spaces <= 0) return self;
1086
1087            StringBuilder ret = new StringBuilder(width);
1088            if (self.Length > 0 && IsSign(self[0])) {
1089                ret.Append(self[0]);
1090                ret.Append('0', spaces);
1091                ret.Append(self.Substring(1));
1092            } else {
1093                ret.Append('0', spaces);
1094                ret.Append(self);
1095            }
1096            return ret.ToString();
1097        }
1098        #endregion
1099
1100        #region operators
1101        [SpecialName]
1102        public static string Add(string self, string other) {
1103            return self + other;
1104        }
1105
1106        [SpecialName]
1107        public static string Add(string self, char other) {
1108            return self + other;
1109        }
1110
1111        [SpecialName]
1112        public static string Add(char self, string other) {
1113            return self + other;
1114        }
1115
1116        [SpecialName]
1117        public static string Mod(CodeContext/*!*/ context, string self, object other) {
1118            return new StringFormatter(context, self, other).Format();
1119        }
1120
1121        [SpecialName]
1122        [return: MaybeNotImplemented]
1123        public static object Mod(CodeContext/*!*/ context, object other, string self) {
1124            string str = other as string;
1125            if (str != null) {
1126                return new StringFormatter(context, str, self).Format();
1127            }
1128
1129            Extensible<string> es = other as Extensible<string>;
1130            if (es != null) {
1131                return new StringFormatter(context, es.Value, self).Format();
1132            }
1133
1134            return NotImplementedType.Value;
1135        }
1136
1137        [SpecialName]
1138        public static string Multiply(string s, int count) {
1139            if (count <= 0) return String.Empty;
1140            if (count == 1) return s;
1141
1142            long size = (long)s.Length * (long)count;
1143            if (size > Int32.MaxValue) throw PythonOps.OverflowError("repeated string is too long");
1144
1145            int sz = s.Length;
1146            if (sz == 1) return new string(s[0], count);
1147
1148            StringBuilder ret = new StringBuilder(sz * count);
1149            ret.Insert(0, s, count);
1150            // the above code is MUCH faster than the simple loop
1151            //for (int i=0; i < count; i++) ret.Append(s);
1152            return ret.ToString();
1153        }
1154
1155        [SpecialName]
1156        public static string Multiply(int other, string self) {
1157            return Multiply(self, other);
1158        }
1159
1160        [SpecialName]
1161        public static object Multiply(string self, [NotNull]Index count) {
1162            return PythonOps.MultiplySequence<string>(Multiply, self, count, true);
1163        }
1164
1165        [SpecialName]
1166        public static object Multiply([NotNull]Index count, string self) {
1167            return PythonOps.MultiplySequence<string>(Multiply, self, count, false);
1168        }
1169
1170        [SpecialName]
1171        public static object Multiply(string self, object count) {
1172            int index;
1173            if (Converter.TryConvertToIndex(count, out index)) {
1174                return Multiply(self, index);
1175            }
1176
1177            throw PythonOps.TypeErrorForUnIndexableObject(count);
1178        }
1179
1180        [SpecialName]
1181        public static object Multiply(object count, string self) {
1182            int index;
1183            if (Converter.TryConvertToIndex(count, out index)) {
1184                return Multiply(index, self);
1185            }
1186
1187            throw PythonOps.TypeErrorForUnIndexableObject(count);
1188        }
1189
1190        [SpecialName]
1191        public static bool GreaterThan(string x, string y) {
1192            return string.CompareOrdinal(x, y) > 0;
1193        }
1194        [SpecialName]
1195        public static bool LessThan(string x, string y) {
1196            return string.CompareOrdinal(x, y) < 0;
1197        }
1198        [SpecialName]
1199        public static bool LessThanOrEqual(string x, string y) {
1200            return string.CompareOrdinal(x, y) <= 0;
1201        }
1202        [SpecialName]
1203        public static bool GreaterThanOrEqual(string x, string y) {
1204            return string.CompareOrdinal(x, y) >= 0;
1205        }
1206        [SpecialName]
1207        public static bool Equals(string x, string y) {
1208            return string.Equals(x, y);
1209        }
1210        [SpecialName]
1211        public static bool NotEquals(string x, string y) {
1212            return !string.Equals(x, y);
1213        }
1214
1215        #endregion
1216
1217        [SpecialName, ImplicitConversionMethod]
1218        public static string ConvertFromChar(char c) {
1219            return RuntimeHelpers.CharToString(c);
1220        }
1221
1222        [SpecialName, ExplicitConversionMethod]
1223        public static char ConvertToChar(string s) {
1224            if (s.Length == 1) return s[0];
1225            throw PythonOps.TypeErrorForTypeMismatch("char", s);
1226        }
1227
1228        [SpecialName, ImplicitConversionMethod]
1229        public static IEnumerable ConvertToIEnumerable(string s) {
1230            return StringOps.GetEnumerable(s);
1231        }
1232
1233        public static int __cmp__(string self, string obj) {
1234            int ret = string.CompareOrdinal(self, obj);
1235            return ret == 0 ? 0 : (ret < 0 ? -1 : +1);
1236        }
1237
1238        public static int __cmp__(string self, ExtensibleString obj) {
1239            int ret = string.CompareOrdinal(self, obj.Value);
1240            return ret == 0 ? 0 : (ret < 0 ? -1 : +1);
1241        }
1242
1243        public static int __cmp__(string self, char obj) {
1244            return (int)(self[0] - (char)obj);
1245        }        
1246
1247        public static object __getnewargs__(CodeContext/*!*/ context, string self) {
1248            if (!Object.ReferenceEquals(self, null)) {
1249                // Cast self to object to avoid exception caused by trying to access SystemState on DefaultContext
1250                return PythonTuple.MakeTuple(StringOps.__new__(context, TypeCache.String, (object)self));
1251            }
1252            throw PythonOps.TypeErrorForBadInstance("__getnewargs__ requires a 'str' object but received a '{0}'", self);
1253        }
1254
1255        public static string __str__(string self) {
1256            return self;
1257        }
1258
1259        public static Extensible<string> __str__(ExtensibleString self) {
1260            return self;
1261        }
1262
1263        #region Internal implementation details
1264
1265        internal static IEnumerable GetEnumerable(string s) {
1266            // make an enumerator that produces strings instead of chars
1267            return new PythonStringEnumerable(s);
1268        }
1269
1270        internal static string Quote(string s) {
1271
1272            bool isUnicode = false;
1273            StringBuilder b = new StringBuilder(s.Length + 5);
1274            char quote = '\'';
1275            if (s.IndexOf('\'') != -1 && s.IndexOf('\"') == -1) {
1276                quote = '\"';
1277            }
1278            b.Append(quote);
1279            b.Append(ReprEncode(s, quote, ref isUnicode));
1280            b.Append(quote);
1281            if (isUnicode) return "u" + b.ToString();
1282            return b.ToString();
1283        }
1284
1285        internal static string ReprEncode(string s, ref bool isUnicode) {
1286            return ReprEncode(s, (char)0, ref isUnicode);
1287        }
1288
1289        internal static bool TryGetEncoding(string name, out Encoding encoding) {
1290#if SILVERLIGHT // EncodingInfo
1291            switch (NormalizeEncodingName(name)) {
1292                case "us_ascii":
1293                case "ascii": encoding = PythonAsciiEncoding.Instance; return true;
1294                case "utf_8": encoding = (Encoding)new EncodingWrapper(Encoding.UTF8, new byte[0]).Clone(); return true;
1295                case "utf_16_le": encoding = (Encoding)new EncodingWrapper(Encoding.Unicode, new byte[0]).Clone(); return true;
1296                case "utf_16_be": encoding = (Encoding)new EncodingWrapper(Encoding.BigEndianUnicode, new byte[0]).Clone(); return true;
1297                case "utf_8_sig": encoding = Encoding.UTF8; return true;
1298            }
1299#else
1300            name = NormalizeEncodingName(name);
1301
1302            EncodingInfoWrapper encInfo;
1303            if (CodecsInfo.Codecs.TryGetValue(name, out encInfo)) {
1304                encoding = (Encoding)encInfo.GetEncoding().Clone();
1305                return true;
1306            }
1307#endif
1308            encoding = null;
1309            return false;
1310        }
1311
1312        internal static byte[] ToByteArray(string s) {
1313            byte[] ret = new byte[s.Length];
1314            for (int i = 0; i < s.Length; i++) {
1315                if (

Large files files are truncated, but you can click here to view the full file