importing messaging-2008 branch to trunk [continued]
[mono.git] / mcs / class / System / System.Text.RegularExpressions / Regex.cs
1 //
2 // assembly:    System
3 // namespace:   System.Text.RegularExpressions
4 // file:        regex.cs
5 //
6 // author:      Dan Lewis (dlewis@gmx.co.uk)
7 //              (c) 2002
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System;
31 using System.Text;
32 using System.Collections;
33 using System.Reflection;
34 using System.Reflection.Emit;
35 using System.Runtime.Serialization;
36
37 using RegularExpression = System.Text.RegularExpressions.Syntax.RegularExpression;
38 using Parser = System.Text.RegularExpressions.Syntax.Parser;
39
40 using System.Diagnostics;
41
42
43 namespace System.Text.RegularExpressions {
44         
45         [Serializable]
46         public partial class Regex : ISerializable {
47
48 #if NET_2_0
49                 private static int cache_size = 15;
50 #endif
51 #if !TARGET_JVM
52                 [MonoTODO]
53                 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname)
54                 {
55                         Regex.CompileToAssembly(regexes, aname, new CustomAttributeBuilder [] {}, null);
56                 }
57
58                 [MonoTODO]
59                 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
60                                                       CustomAttributeBuilder [] attribs)
61                 {
62                         Regex.CompileToAssembly(regexes, aname, attribs, null);
63                 }
64
65                 [MonoTODO]
66                 public static void CompileToAssembly (RegexCompilationInfo [] regexes, AssemblyName aname,
67                                                       CustomAttributeBuilder [] attribs, string resourceFile)
68                 {
69                         throw new NotImplementedException ();
70                         // TODO : Make use of attribs and resourceFile parameters
71                         /*
72                         AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (aname, AssemblyBuilderAccess.RunAndSave);
73                         ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("InnerRegexModule",aname.Name);
74                         Parser psr = new Parser ();     
75                         
76                         System.Console.WriteLine("CompileToAssembly");
77                                
78                         for(int i=0; i < regexes.Length; i++)
79                                 {
80                                         System.Console.WriteLine("Compiling expression :" + regexes[i].Pattern);
81                                         RegularExpression re = psr.ParseRegularExpression (regexes[i].Pattern, regexes[i].Options);
82                                         
83                                         // compile
84                                                                                 
85                                         CILCompiler cmp = new CILCompiler (modBuilder, i);
86                                         bool reverse = (regexes[i].Options & RegexOptions.RightToLeft) !=0;
87                                         re.Compile (cmp, reverse);
88                                         cmp.Close();
89                                         
90                                 }
91                        
92
93                         // Define a runtime class with specified name and attributes.
94                         TypeBuilder builder = modBuilder.DefineType("ITest");
95                         builder.CreateType();
96                         asmBuilder.Save(aname.Name);
97                         */
98                 }
99 #endif
100                 
101                 public static string Escape (string str)
102                 {
103                         return Parser.Escape (str);
104                 }
105
106                 public static string Unescape (string str)
107                 {
108                         return Parser.Unescape (str);
109                 }
110
111                 public static bool IsMatch (string input, string pattern)
112                 {
113                         return IsMatch (input, pattern, RegexOptions.None);
114                 }
115
116                 public static bool IsMatch (string input, string pattern, RegexOptions options)
117                 {
118                         Regex re = new Regex (pattern, options);
119                         return re.IsMatch (input);
120                 }
121
122                 public static Match Match (string input, string pattern)
123                 {
124                         return Regex.Match (input, pattern, RegexOptions.None);
125                 }
126
127                 public static Match Match (string input, string pattern, RegexOptions options)
128                 {
129                         Regex re = new Regex (pattern, options);
130                         return re.Match (input);
131                 }
132
133                 public static MatchCollection Matches (string input, string pattern)
134                 {
135                         return Matches (input, pattern, RegexOptions.None);
136                 }
137
138                 public static MatchCollection Matches (string input, string pattern, RegexOptions options)
139                 {
140                         Regex re = new Regex (pattern, options);
141                         return re.Matches (input);
142                 }
143
144                 public static string Replace (string input, string pattern, MatchEvaluator evaluator)
145                 {
146                         return Regex.Replace (input, pattern, evaluator, RegexOptions.None);
147                 }
148
149                 public static string Replace (string input, string pattern, MatchEvaluator evaluator,
150                                               RegexOptions options)
151                 {
152                         Regex re = new Regex (pattern, options);
153                         return re.Replace (input, evaluator);
154                 }
155
156                 public static string Replace (string input, string pattern, string replacement)
157                 {
158                         return Regex.Replace (input, pattern, replacement, RegexOptions.None);
159                 }
160
161                 public static string Replace (string input, string pattern, string replacement,
162                                               RegexOptions options)
163                 {
164                         Regex re = new Regex (pattern, options);
165                         return re.Replace (input, replacement);
166                 }
167
168                 public static string [] Split (string input, string pattern)
169                 {
170                         return Regex.Split (input, pattern, RegexOptions.None);
171                 }
172
173                 public static string [] Split (string input, string pattern, RegexOptions options)
174                 {
175                         Regex re = new Regex (pattern, options);
176                         return re.Split (input);
177                 }
178
179 #if NET_2_0
180                 static FactoryCache cache = new FactoryCache (15);
181                 public static int CacheSize {
182                         get { return cache.Capacity; }
183                         set {
184                                 if (value < 0)
185                                         throw new ArgumentOutOfRangeException ("CacheSize");
186
187                                 cache.Capacity = value; 
188                         }
189                 }
190 #else
191                 static FactoryCache cache = new FactoryCache (200);
192 #endif
193
194                 // private
195
196
197                 // constructors
198
199                 // This constructor is used by compiled regular expressions that are
200                 // classes derived from Regex class. No initialization required.
201                 protected Regex ()
202                 {
203                 }
204
205                 public Regex (string pattern) : this (pattern, RegexOptions.None)
206                 {
207                 }
208
209                 public Regex (string pattern, RegexOptions options)
210                 {
211                         this.pattern = pattern;
212                         this.roptions = options;
213                         Init ();
214                 }
215 #if !TARGET_JVM
216                 private void Init ()
217                 {
218                         this.machineFactory = cache.Lookup (this.pattern, this.roptions);
219
220                         if (this.machineFactory == null) {
221                                 InitNewRegex();
222                         } else {
223                                 this.group_count = this.machineFactory.GroupCount;
224                                 this.mapping = this.machineFactory.Mapping;
225                                 this._groupNumberToNameMap = this.machineFactory.NamesMapping;
226                         }
227                 }
228 #endif
229
230                 private void InitNewRegex () 
231                 {
232                         this.machineFactory = CreateMachineFactory (this.pattern, this.roptions);
233                         cache.Add (this.pattern, this.roptions, this.machineFactory);
234                         this.group_count = machineFactory.GroupCount;
235                         this.mapping = machineFactory.Mapping;
236                         this._groupNumberToNameMap = this.machineFactory.NamesMapping;
237                 }
238
239                 static readonly bool old_rx = Environment.GetEnvironmentVariable ("MONO_OLD_RX") != null;
240
241                 private static IMachineFactory CreateMachineFactory (string pattern, RegexOptions options) 
242                 {
243                         Parser psr = new Parser ();
244                         RegularExpression re = psr.ParseRegularExpression (pattern, options);
245
246                         ICompiler cmp;
247                         if (!old_rx) {
248                                 if ((options & RegexOptions.Compiled) != 0)
249                                         cmp = new CILCompiler ();
250                                 else
251                                         cmp = new RxCompiler ();
252                         } else {
253                                 cmp = new PatternCompiler ();
254                         }
255
256                         re.Compile (cmp, (options & RegexOptions.RightToLeft) != 0);
257
258                         IMachineFactory machineFactory = cmp.GetMachineFactory ();
259                         machineFactory.Mapping = psr.GetMapping ();
260                         machineFactory.NamesMapping = GetGroupNamesArray (machineFactory.GroupCount, machineFactory.Mapping);
261
262                         return machineFactory;
263                 }
264
265 #if NET_2_0
266                 protected
267 #else
268                 private
269 #endif
270                 Regex (SerializationInfo info, StreamingContext context) :
271                         this (info.GetString ("pattern"), 
272                               (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
273                 {
274                 }
275
276 #if ONLY_1_1 && !TARGET_JVM
277                 // fixes public API signature
278                 ~Regex ()
279                 {
280                 }
281 #endif
282                 // public instance properties
283                 
284                 public RegexOptions Options {
285                         get { return roptions; }
286                 }
287
288                 public bool RightToLeft {
289                         get { return (roptions & RegexOptions.RightToLeft) != 0; }
290                 }
291
292                 // public instance methods
293                 
294                 public string [] GetGroupNames ()
295                 {
296                         string [] names = new string [mapping.Count];
297                         mapping.Keys.CopyTo (names, 0);
298
299                         return names;
300                 }
301
302                 public int[] GetGroupNumbers ()
303                 {
304                         int[] numbers = new int [mapping.Count];
305                         mapping.Values.CopyTo (numbers, 0);
306
307                         return numbers;
308                 }
309
310                 public string GroupNameFromNumber (int i)
311                 {
312                         if (i < 0 || i > group_count)
313                                 return "";
314
315                         return _groupNumberToNameMap [i];
316                 }
317
318                 public int GroupNumberFromName (string name)
319                 {
320                         if (mapping.Contains (name))
321                                 return (int) mapping [name];
322
323                         return -1;
324                 }
325
326                 // match methods
327                 
328                 public bool IsMatch (string input)
329                 {
330                         return IsMatch (input, RightToLeft ? input.Length : 0);
331                 }
332
333                 public bool IsMatch (string input, int startat)
334                 {
335                         return Match (input, startat).Success;
336                 }
337
338                 public Match Match (string input)
339                 {
340                         return Match (input, RightToLeft ? input.Length : 0);
341                 }
342
343                 public Match Match (string input, int startat)
344                 {
345                         return CreateMachine ().Scan (this, input, startat, input.Length);
346                 }
347
348                 public Match Match (string input, int startat, int length)
349                 {
350                         return CreateMachine ().Scan (this, input, startat, startat + length);
351                 }
352
353                 public MatchCollection Matches (string input)
354                 {
355                         return Matches (input, RightToLeft ? input.Length : 0);
356                 }
357
358                 public MatchCollection Matches (string input, int startat)
359                 {
360                         Match m = Match (input, startat);
361                         return new MatchCollection (m);
362                 }
363
364                 // replace methods
365
366                 public string Replace (string input, MatchEvaluator evaluator)
367                 {
368                         return Replace (input, evaluator, Int32.MaxValue, RightToLeft ? input.Length : 0);
369                 }
370
371                 public string Replace (string input, MatchEvaluator evaluator, int count)
372                 {
373                         return Replace (input, evaluator, count, RightToLeft ? input.Length : 0);
374                 }
375
376                 class Adapter {
377                         MatchEvaluator ev;
378                         public Adapter (MatchEvaluator ev) { this.ev = ev; }
379                         public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
380                 }
381
382                 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
383                 {
384                         BaseMachine m = (BaseMachine)CreateMachine ();
385
386                         if (RightToLeft)
387                                 return m.RTLReplace (this, input, evaluator, count, startat);
388
389                         // NOTE: If this is a cause of a lot of allocations, we can convert it to
390                         //       use a ThreadStatic allocation mitigator
391                         Adapter a = new Adapter (evaluator);
392
393                         return m.LTRReplace (this, input, new BaseMachine.MatchAppendEvaluator (a.Evaluate),
394                                                                  count, startat);
395                 }
396
397                 public string Replace (string input, string replacement)
398                 {
399                         return Replace (input, replacement, Int32.MaxValue, RightToLeft ? input.Length : 0);
400                 }
401
402                 public string Replace (string input, string replacement, int count)
403                 {
404                         return Replace (input, replacement, count, RightToLeft ? input.Length : 0);
405                 }
406
407                 public string Replace (string input, string replacement, int count, int startat)
408                 {
409                         return CreateMachine ().Replace (this, input, replacement, count, startat);
410                 }
411
412                 // split methods
413
414                 public string [] Split (string input)
415                 {
416                         return Split (input, Int32.MaxValue, RightToLeft ? input.Length : 0);
417                 }
418
419                 public string [] Split (string input, int count)
420                 {
421                         return Split (input, count, RightToLeft ? input.Length : 0);
422                 }
423
424                 public string [] Split (string input, int count, int startat)
425                 {
426                         return CreateMachine ().Split (this, input, count, startat);
427                 }
428
429                 // This method is called at the end of the constructor of compiled
430                 // regular expression classes to do internal initialization.
431                 protected void InitializeReferences ()
432                 {
433                         if (refsInitialized)
434                                 throw new NotSupportedException ("This operation is only allowed once per object.");
435
436                         refsInitialized = true;
437
438                         // Compile pattern that results in performance loss as existing
439                         // CIL code is ignored but provides support for regular
440                         // expressions compiled to assemblies.
441                         Init ();
442                 }
443
444                 protected bool UseOptionC ()
445                 {
446                         return ((roptions & RegexOptions.Compiled) != 0);
447                 }
448
449                 protected bool UseOptionR ()
450                 {
451                         return ((roptions & RegexOptions.RightToLeft) != 0);
452                 }
453
454                 // object methods
455                 
456                 public override string ToString ()
457                 {
458                         return pattern;
459                 }
460
461                 // ISerializable interface
462                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
463                 {
464                         info.AddValue ("pattern", this.ToString (), typeof (string));
465                         info.AddValue ("options", this.Options, typeof (RegexOptions));
466                 }
467
468                 // internal
469
470                 internal int GroupCount {
471                         get { return group_count; }
472                 }
473
474                 // private
475
476                 private IMachine CreateMachine ()
477                 {
478                         return machineFactory.NewInstance ();
479                 }
480
481                 private static string [] GetGroupNamesArray (int groupCount, IDictionary mapping) 
482                 {
483                         string [] groupNumberToNameMap = new string [groupCount + 1];
484                         foreach (string name in mapping.Keys) {
485                                 groupNumberToNameMap [(int) mapping [name]] = name;
486                         }
487                         return groupNumberToNameMap;
488                 }
489                 
490                 private IMachineFactory machineFactory;
491                 private IDictionary mapping;
492                 private int group_count;
493                 private bool refsInitialized;
494                 private string [] _groupNumberToNameMap;
495
496                 
497                 // protected members
498
499                 protected internal string pattern;
500                 protected internal RegexOptions roptions;
501                 
502                 // MS undocumented members
503 #if NET_2_1
504                 [MonoTODO]
505                 protected internal System.Collections.Generic.Dictionary<string, int> capnames;
506                 [MonoTODO]
507                 protected internal System.Collections.Generic.Dictionary<int, int> caps;
508 #else
509                 [MonoTODO]
510                 protected internal System.Collections.Hashtable capnames;
511                 [MonoTODO]
512                 protected internal System.Collections.Hashtable caps;
513 #endif
514                 [MonoTODO]
515                 protected internal int capsize;
516                 [MonoTODO]
517                 protected internal string [] capslist;
518                 [MonoTODO]
519                 protected internal RegexRunnerFactory factory;
520         }
521 }