Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / System / System.Text.RegularExpressions / Regex.cs
index 592b3941c0c5e482d7a05adbe3473cde6b8c063b..378a3b872fe2cd8379e6da72e5983990f0525824 100644 (file)
@@ -45,7 +45,7 @@ namespace System.Text.RegularExpressions {
        [Serializable]
        public partial class Regex : ISerializable {
 
-#if !TARGET_JVM
+#if !TARGET_JVM && !FULL_AOT_RUNTIME
                [MonoTODO]
                public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname)
                {
@@ -97,11 +97,15 @@ namespace System.Text.RegularExpressions {
                
                public static string Escape (string str)
                {
+                       if (str == null)
+                               throw new ArgumentNullException ("str");
                        return Parser.Escape (str);
                }
 
                public static string Unescape (string str)
                {
+                       if (str == null)
+                               throw new ArgumentNullException ("str");
                        return Parser.Unescape (str);
                }
 
@@ -173,7 +177,6 @@ namespace System.Text.RegularExpressions {
                        return re.Split (input);
                }
 
-#if NET_2_0
                static FactoryCache cache = new FactoryCache (15);
                public static int CacheSize {
                        get { return cache.Capacity; }
@@ -184,9 +187,6 @@ namespace System.Text.RegularExpressions {
                                cache.Capacity = value; 
                        }
                }
-#else
-               static FactoryCache cache = new FactoryCache (200);
-#endif
 
                // private
 
@@ -205,10 +205,44 @@ namespace System.Text.RegularExpressions {
 
                public Regex (string pattern, RegexOptions options)
                {
+                       if (pattern == null)
+                               throw new ArgumentNullException ("pattern");
+                       validate_options (options);
                        this.pattern = pattern;
                        this.roptions = options;
                        Init ();
                }
+
+               static void validate_options (RegexOptions options)
+               {
+                       const RegexOptions allopts =
+                               RegexOptions.None |
+                               RegexOptions.IgnoreCase |
+                               RegexOptions.Multiline |
+                               RegexOptions.ExplicitCapture |
+#if MOBILE || !NET_2_1
+                               RegexOptions.Compiled |
+#endif
+                               RegexOptions.Singleline |
+                               RegexOptions.IgnorePatternWhitespace |
+                               RegexOptions.RightToLeft |
+                               RegexOptions.ECMAScript |
+                               RegexOptions.CultureInvariant;
+
+                       const RegexOptions ecmaopts =
+                               RegexOptions.IgnoreCase |
+                               RegexOptions.Multiline |
+#if MOBILE || !NET_2_1
+                               RegexOptions.Compiled |
+#endif
+                               RegexOptions.ECMAScript;
+
+                       if ((options & ~allopts) != 0)
+                               throw new ArgumentOutOfRangeException ("options");
+                       if ((options & RegexOptions.ECMAScript) != 0 && (options & ~ecmaopts) != 0)
+                               throw new ArgumentOutOfRangeException ("options");
+               }
+
 #if !TARGET_JVM
                private void Init ()
                {
@@ -218,8 +252,9 @@ namespace System.Text.RegularExpressions {
                                InitNewRegex();
                        } else {
                                this.group_count = this.machineFactory.GroupCount;
+                               this.gap = this.machineFactory.Gap;
                                this.mapping = this.machineFactory.Mapping;
-                               this._groupNumberToNameMap = this.machineFactory.NamesMapping;
+                               this.group_names = this.machineFactory.NamesMapping;
                        }
                }
 #endif
@@ -229,12 +264,14 @@ namespace System.Text.RegularExpressions {
                        this.machineFactory = CreateMachineFactory (this.pattern, this.roptions);
                        cache.Add (this.pattern, this.roptions, this.machineFactory);
                        this.group_count = machineFactory.GroupCount;
+                       this.gap = this.machineFactory.Gap;
                        this.mapping = machineFactory.Mapping;
-                       this._groupNumberToNameMap = this.machineFactory.NamesMapping;
+                       this.group_names = this.machineFactory.NamesMapping;
                }
 
 #if !NET_2_1
-               // The new rx engine has blocking bugs like
+               // The new rx engine seems to be working now, but
+               // potential problems are being tracked down here:
                // https://bugzilla.novell.com/show_bug.cgi?id=470827
                static readonly bool old_rx =
                        Environment.GetEnvironmentVariable ("MONO_NEW_RX") == null;
@@ -262,29 +299,20 @@ namespace System.Text.RegularExpressions {
                        re.Compile (cmp, (options & RegexOptions.RightToLeft) != 0);
 
                        IMachineFactory machineFactory = cmp.GetMachineFactory ();
-                       machineFactory.Mapping = psr.GetMapping ();
+                       Hashtable mapping = new Hashtable ();
+                       machineFactory.Gap = psr.GetMapping (mapping);
+                       machineFactory.Mapping = mapping;
                        machineFactory.NamesMapping = GetGroupNamesArray (machineFactory.GroupCount, machineFactory.Mapping);
 
                        return machineFactory;
                }
 
-#if NET_2_0
-               protected
-#else
-               private
-#endif
-               Regex (SerializationInfo info, StreamingContext context) :
+               protected Regex (SerializationInfo info, StreamingContext context) :
                        this (info.GetString ("pattern"), 
                              (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
                {
                }
 
-#if ONLY_1_1 && !TARGET_JVM
-               // fixes public API signature
-               ~Regex ()
-               {
-               }
-#endif
                // public instance properties
                
                public RegexOptions Options {
@@ -300,16 +328,14 @@ namespace System.Text.RegularExpressions {
                public string [] GetGroupNames ()
                {
                        string [] names = new string [1 + group_count];
-                       Array.Copy (_groupNumberToNameMap, names, 1 + group_count);
+                       Array.Copy (group_names, names, 1 + group_count);
                        return names;
                }
 
-               public int[] GetGroupNumbers ()
+               public int [] GetGroupNumbers ()
                {
-                       int[] numbers = new int [1 + group_count];
-                       for (int i = 0; i <= group_count; ++i)
-                               numbers [i] = i;
-                       // FIXME: needs to handle arbitrarily numbered groups '(?<43>abc)'
+                       int [] numbers = new int [1 + group_count];
+                       Array.Copy (GroupNumbers, numbers, 1 + group_count);
                        return numbers;
                }
 
@@ -319,31 +345,38 @@ namespace System.Text.RegularExpressions {
                        if (i < 0)
                                return "";
 
-                       return _groupNumberToNameMap [i];
+                       return group_names [i];
                }
 
                public int GroupNumberFromName (string name)
                {
-                       if (mapping.Contains (name))
-                               return (int) mapping [name];
-
-                       return -1;
+                       if (!mapping.Contains (name))
+                               return -1;
+                       int i = (int) mapping [name];
+                       if (i >= gap)
+                               i = Int32.Parse (name);
+                       return i;
                }
 
                internal int GetGroupIndex (int number)
                {
-                       int gap = group_count + 1;
                        if (number < gap)
                                return number;
-                       // FIXME: handle arbitrarily numbered groups here
-                       return -1;
+                       if (gap > group_count)
+                               return -1;
+                       return Array.BinarySearch (GroupNumbers, gap, group_count - gap + 1, number);
+               }
+
+               int default_startat (string input)
+               {
+                       return (RightToLeft && input != null) ? input.Length : 0;
                }
 
                // match methods
                
                public bool IsMatch (string input)
                {
-                       return IsMatch (input, RightToLeft ? input.Length : 0);
+                       return IsMatch (input, default_startat (input));
                }
 
                public bool IsMatch (string input, int startat)
@@ -353,22 +386,32 @@ namespace System.Text.RegularExpressions {
 
                public Match Match (string input)
                {
-                       return Match (input, RightToLeft ? input.Length : 0);
+                       return Match (input, default_startat (input));
                }
 
                public Match Match (string input, int startat)
                {
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (startat < 0 || startat > input.Length)
+                               throw new ArgumentOutOfRangeException ("startat");
                        return CreateMachine ().Scan (this, input, startat, input.Length);
                }
 
-               public Match Match (string input, int startat, int length)
+               public Match Match (string input, int beginning, int length)
                {
-                       return CreateMachine ().Scan (this, input, startat, startat + length);
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (beginning < 0 || beginning > input.Length)
+                               throw new ArgumentOutOfRangeException ("beginning");
+                       if (length < 0 || length > input.Length - beginning)
+                               throw new ArgumentOutOfRangeException ("length");
+                       return CreateMachine ().Scan (this, input, beginning, beginning + length);
                }
 
                public MatchCollection Matches (string input)
                {
-                       return Matches (input, RightToLeft ? input.Length : 0);
+                       return Matches (input, default_startat (input));
                }
 
                public MatchCollection Matches (string input, int startat)
@@ -381,12 +424,12 @@ namespace System.Text.RegularExpressions {
 
                public string Replace (string input, MatchEvaluator evaluator)
                {
-                       return Replace (input, evaluator, Int32.MaxValue, RightToLeft ? input.Length : 0);
+                       return Replace (input, evaluator, Int32.MaxValue, default_startat (input));
                }
 
                public string Replace (string input, MatchEvaluator evaluator, int count)
                {
-                       return Replace (input, evaluator, count, RightToLeft ? input.Length : 0);
+                       return Replace (input, evaluator, count, default_startat (input));
                }
 
                class Adapter {
@@ -398,9 +441,13 @@ namespace System.Text.RegularExpressions {
                public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
                {
                        if (input == null)
-                               throw new ArgumentNullException ("null");
+                               throw new ArgumentNullException ("input");
                        if (evaluator == null)
                                throw new ArgumentNullException ("evaluator");
+                       if (count < -1)
+                               throw new ArgumentOutOfRangeException ("count");
+                       if (startat < 0 || startat > input.Length)
+                               throw new ArgumentOutOfRangeException ("startat");
 
                        BaseMachine m = (BaseMachine)CreateMachine ();
 
@@ -417,16 +464,25 @@ namespace System.Text.RegularExpressions {
 
                public string Replace (string input, string replacement)
                {
-                       return Replace (input, replacement, Int32.MaxValue, RightToLeft ? input.Length : 0);
+                       return Replace (input, replacement, Int32.MaxValue, default_startat (input));
                }
 
                public string Replace (string input, string replacement, int count)
                {
-                       return Replace (input, replacement, count, RightToLeft ? input.Length : 0);
+                       return Replace (input, replacement, count, default_startat (input));
                }
 
                public string Replace (string input, string replacement, int count, int startat)
                {
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (replacement == null)
+                               throw new ArgumentNullException ("replacement");
+                       if (count < -1)
+                               throw new ArgumentOutOfRangeException ("count");
+                       if (startat < 0 || startat > input.Length)
+                               throw new ArgumentOutOfRangeException ("startat");
+
                        return CreateMachine ().Replace (this, input, replacement, count, startat);
                }
 
@@ -434,16 +490,23 @@ namespace System.Text.RegularExpressions {
 
                public string [] Split (string input)
                {
-                       return Split (input, Int32.MaxValue, RightToLeft ? input.Length : 0);
+                       return Split (input, Int32.MaxValue, default_startat (input));
                }
 
                public string [] Split (string input, int count)
                {
-                       return Split (input, count, RightToLeft ? input.Length : 0);
+                       return Split (input, count, default_startat (input));
                }
 
                public string [] Split (string input, int count, int startat)
                {
+                       if (input == null)
+                               throw new ArgumentNullException ("input");
+                       if (count < 0)
+                               throw new ArgumentOutOfRangeException ("count");
+                       if (startat < 0 || startat > input.Length)
+                               throw new ArgumentOutOfRangeException ("startat");
+
                        return CreateMachine ().Split (this, input, count, startat);
                }
 
@@ -492,6 +555,10 @@ namespace System.Text.RegularExpressions {
                        get { return group_count; }
                }
 
+               internal int Gap {
+                       get { return gap; }
+               }
+
                // private
 
                private IMachine CreateMachine ()
@@ -501,19 +568,34 @@ namespace System.Text.RegularExpressions {
 
                private static string [] GetGroupNamesArray (int groupCount, IDictionary mapping) 
                {
-                       string [] groupNumberToNameMap = new string [groupCount + 1];
-                       foreach (string name in mapping.Keys) {
-                               groupNumberToNameMap [(int) mapping [name]] = name;
+                       string [] group_names = new string [groupCount + 1];
+                       IDictionaryEnumerator de = mapping.GetEnumerator ();
+                       while (de.MoveNext ())
+                               group_names [(int) de.Value] = (string) de.Key;
+                       return group_names;
+               }
+
+               private int [] GroupNumbers {
+                       get {
+                               if (group_numbers == null) {
+                                       group_numbers = new int [1 + group_count];
+                                       for (int i = 0; i < gap; ++i)
+                                               group_numbers [i] = i;
+                                       for (int i = gap; i <= group_count; ++i)
+                                               group_numbers [i] = Int32.Parse (group_names [i]);
+                                       return group_numbers;
+                               }
+                               return group_numbers;
                        }
-                       return groupNumberToNameMap;
                }
-               
+
                private IMachineFactory machineFactory;
                private IDictionary mapping;
                private int group_count;
+               private int gap;
                private bool refsInitialized;
-               private string [] _groupNumberToNameMap;
-
+               private string [] group_names;
+               private int [] group_numbers;
                
                // protected members