New test.
[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 class Regex : ISerializable {
47
48 #if NET_2_0
49                 private static int cache_size = 15;
50 #endif
51
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                 
100                 public static string Escape (string str)
101                 {
102                         return Parser.Escape (str);
103                 }
104
105                 public static string Unescape (string str)
106                 {
107                         return Parser.Unescape (str);
108                 }
109
110                 public static bool IsMatch (string input, string pattern)
111                 {
112                         return IsMatch (input, pattern, RegexOptions.None);
113                 }
114
115                 public static bool IsMatch (string input, string pattern, RegexOptions options)
116                 {
117                         Regex re = new Regex (pattern, options);
118                         return re.IsMatch (input);
119                 }
120
121                 public static Match Match (string input, string pattern)
122                 {
123                         return Regex.Match (input, pattern, RegexOptions.None);
124                 }
125
126                 public static Match Match (string input, string pattern, RegexOptions options)
127                 {
128                         Regex re = new Regex (pattern, options);
129                         return re.Match (input);
130                 }
131
132                 public static MatchCollection Matches (string input, string pattern)
133                 {
134                         return Matches (input, pattern, RegexOptions.None);
135                 }
136
137                 public static MatchCollection Matches (string input, string pattern, RegexOptions options)
138                 {
139                         Regex re = new Regex (pattern, options);
140                         return re.Matches (input);
141                 }
142
143                 public static string Replace (string input, string pattern, MatchEvaluator evaluator)
144                 {
145                         return Regex.Replace (input, pattern, evaluator, RegexOptions.None);
146                 }
147
148                 public static string Replace (string input, string pattern, MatchEvaluator evaluator,
149                                               RegexOptions options)
150                 {
151                         Regex re = new Regex (pattern, options);
152                         return re.Replace (input, evaluator);
153                 }
154
155                 public static string Replace (string input, string pattern, string replacement)
156                 {
157                         return Regex.Replace (input, pattern, replacement, RegexOptions.None);
158                 }
159
160                 public static string Replace (string input, string pattern, string replacement,
161                                               RegexOptions options)
162                 {
163                         Regex re = new Regex (pattern, options);
164                         return re.Replace (input, replacement);
165                 }
166
167                 public static string [] Split (string input, string pattern)
168                 {
169                         return Regex.Split (input, pattern, RegexOptions.None);
170                 }
171
172                 public static string [] Split (string input, string pattern, RegexOptions options)
173                 {
174                         Regex re = new Regex (pattern, options);
175                         return re.Split (input);
176                 }
177
178 #if NET_2_0
179                 [MonoTODO ("should be used somewhere ? FactoryCache ?")]
180                 public static int CacheSize {
181                         get { return cache_size; }
182                         set {
183                                 if (value < 0)
184                                         throw new ArgumentOutOfRangeException ("CacheSize");
185                                 cache_size = value;
186                         }
187                 }
188 #endif
189
190                 // private
191
192                 private static FactoryCache cache = new FactoryCache (200);     // TODO put some meaningful number here
193
194                 // constructors
195
196                 // This constructor is used by compiled regular expressions that are
197                 // classes derived from Regex class. No initialization required.
198                 protected Regex ()
199                 {
200                 }
201
202                 public Regex (string pattern) : this (pattern, RegexOptions.None)
203                 {
204                 }
205
206                 public Regex (string pattern, RegexOptions options)
207                 {
208                         this.pattern = pattern;
209                         this.roptions = options;
210                         Init ();
211                 }
212
213                 private void Init ()
214                 {
215                         this.machineFactory = cache.Lookup (this.pattern, this.roptions);
216
217                         if (this.machineFactory == null) {
218                                 // parse and install group mapping
219
220                                 Parser psr = new Parser ();
221                                 RegularExpression re = psr.ParseRegularExpression (this.pattern, this.roptions);
222                                 this.group_count = re.GroupCount;
223                                 this.mapping = psr.GetMapping ();
224
225                                 // compile
226                                 
227                                 ICompiler cmp;
228                                 //if ((this.roptions & RegexOptions.Compiled) != 0)
229                                 //      //throw new Exception ("Not implemented.");
230                                 //      cmp = new CILCompiler ();
231                                 //else
232                                 cmp = new PatternCompiler ();
233
234                                 re.Compile (cmp, RightToLeft);
235
236                                 // install machine factory and add to pattern cache
237
238                                 this.machineFactory = cmp.GetMachineFactory ();
239                                 this.machineFactory.Mapping = mapping;
240                                 cache.Add (this.pattern, this.roptions, this.machineFactory);
241                         } else {
242                                 this.group_count = this.machineFactory.GroupCount;
243                                 this.mapping = this.machineFactory.Mapping;
244                         }
245                 }
246
247 #if NET_2_0
248                 protected
249 #else
250                 private
251 #endif
252                 Regex (SerializationInfo info, StreamingContext context) :
253                         this (info.GetString ("pattern"), 
254                               (RegexOptions) info.GetValue ("options", typeof (RegexOptions)))
255                 {
256                 }
257
258 #if NET_1_1 && !TARGET_JVM
259                 // fixes public API signature
260                 ~Regex ()
261                 {
262                 }
263 #endif
264                 // public instance properties
265                 
266                 public RegexOptions Options {
267                         get { return roptions; }
268                 }
269
270                 public bool RightToLeft {
271                         get { return (roptions & RegexOptions.RightToLeft) != 0; }
272                 }
273
274                 // public instance methods
275                 
276                 public string [] GetGroupNames ()
277                 {
278                         string [] names = new string [mapping.Count];
279                         mapping.Keys.CopyTo (names, 0);
280
281                         return names;
282                 }
283
284                 public int[] GetGroupNumbers ()
285                 {
286                         int[] numbers = new int [mapping.Count];
287                         mapping.Values.CopyTo (numbers, 0);
288
289                         return numbers;
290                 }
291
292                 public string GroupNameFromNumber (int i)
293                 {
294                         if (i > group_count)
295                                 return "";
296                 
297                         foreach (string name in mapping.Keys) {
298                                 if ((int) mapping [name] == i)
299                                         return name;
300                         }
301
302                         return "";
303                 }
304
305                 public int GroupNumberFromName (string name)
306                 {
307                         if (mapping.Contains (name))
308                                 return (int) mapping [name];
309
310                         return -1;
311                 }
312
313                 // match methods
314                 
315                 public bool IsMatch (string input)
316                 {
317                         return IsMatch (input, RightToLeft ? input.Length : 0);
318                 }
319
320                 public bool IsMatch (string input, int startat)
321                 {
322                         return Match (input, startat).Success;
323                 }
324
325                 public Match Match (string input)
326                 {
327                         return Match (input, RightToLeft ? input.Length : 0);
328                 }
329
330                 public Match Match (string input, int startat)
331                 {
332                         return CreateMachine ().Scan (this, input, startat, input.Length);
333                 }
334
335                 public Match Match (string input, int startat, int length)
336                 {
337                         return CreateMachine ().Scan (this, input, startat, startat + length);
338                 }
339
340                 public MatchCollection Matches (string input)
341                 {
342                         return Matches (input, RightToLeft ? input.Length : 0);
343                 }
344
345                 public MatchCollection Matches (string input, int startat)
346                 {
347                         Match m = Match (input, startat);
348                         return new MatchCollection (m);
349                 }
350
351                 // replace methods
352
353                 public string Replace (string input, MatchEvaluator evaluator)
354                 {
355                         return Replace (input, evaluator, Int32.MaxValue, RightToLeft ? input.Length : 0);
356                 }
357
358                 public string Replace (string input, MatchEvaluator evaluator, int count)
359                 {
360                         return Replace (input, evaluator, count, RightToLeft ? input.Length : 0);
361                 }
362
363                 class Adapter {
364                         MatchEvaluator ev;
365                         public Adapter (MatchEvaluator ev) { this.ev = ev; }
366                         public void Evaluate (Match m, StringBuilder sb) { sb.Append (ev (m)); }
367                 }
368
369                 delegate void MatchAppendEvaluator (Match match, StringBuilder sb);
370
371                 public string Replace (string input, MatchEvaluator evaluator, int count, int startat)
372                 {
373                         Adapter a = new Adapter (evaluator);
374                         return Replace (input, new MatchAppendEvaluator (a.Evaluate), count, startat);
375                 }
376
377                 string Replace (string input, MatchAppendEvaluator evaluator, int count, int startat)
378                 {
379                         StringBuilder result = new StringBuilder ();
380                         int ptr = startat;
381                         int counter = count;
382
383                         result.Append (input, 0, ptr);
384
385                         Match m = Match (input, startat);
386                         while (m.Success) {
387                                 if (count != -1)
388                                         if(counter -- <= 0)
389                                                 break;
390                                 if (m.Index < ptr)
391                                         throw new SystemException ("how");
392                                 result.Append (input, ptr, m.Index - ptr);
393                                 evaluator (m, result);
394
395                                 ptr = m.Index + m.Length;
396                                 m = m.NextMatch ();
397                         }
398                         
399                         if (ptr == 0)
400                                 return input;
401                         
402                         result.Append (input, ptr, input.Length - ptr);
403
404                         return result.ToString ();
405                 }
406
407                 public string Replace (string input, string replacement)
408                 {
409                         return Replace (input, replacement, Int32.MaxValue, RightToLeft ? input.Length : 0);
410                 }
411
412                 public string Replace (string input, string replacement, int count)
413                 {
414                         return Replace (input, replacement, count, RightToLeft ? input.Length : 0);
415                 }
416
417                 public string Replace (string input, string replacement, int count, int startat)
418                 {
419                         ReplacementEvaluator ev = new ReplacementEvaluator (this, replacement);
420                         return Replace (input, new MatchAppendEvaluator (ev.EvaluateAppend), count, startat);
421                 }
422
423                 // split methods
424
425                 public string [] Split (string input)
426                 {
427                         return Split (input, Int32.MaxValue, RightToLeft ? input.Length : 0);
428                 }
429
430                 public string [] Split (string input, int count)
431                 {
432                         return Split (input, count, RightToLeft ? input.Length : 0);
433                 }
434
435                 public string [] Split (string input, int count, int startat)
436                 {
437                         ArrayList splits = new ArrayList ();
438                         if (count == 0)
439                                 count = Int32.MaxValue;
440
441                         int ptr = startat;
442                         Match m = null;
443                         while (--count > 0) {
444                                 if (m != null)
445                                         m = m.NextMatch ();
446                                 else
447                                         m = Match (input, ptr);
448
449                                 if (!m.Success)
450                                         break;
451                         
452                                 if (RightToLeft)
453                                         splits.Add (input.Substring (m.Index + m.Length, ptr - m.Index - m.Length));
454                                 else
455                                         splits.Add (input.Substring (ptr, m.Index - ptr));
456                                         
457                                 int gcount = m.Groups.Count;
458                                 for (int gindex = 1; gindex < gcount; gindex++) {
459                                         Group grp = m.Groups [gindex];
460                                         splits.Add (input.Substring (grp.Index, grp.Length));
461                                 }
462
463                                 if (RightToLeft)
464                                         ptr = m.Index; 
465                                 else
466                                         ptr = m.Index + m.Length;
467                                         
468                         }
469
470                         if (RightToLeft && ptr >= 0)
471                                 splits.Add (input.Substring (0, ptr));
472                         if (!RightToLeft && ptr <= input.Length)
473                                 splits.Add (input.Substring (ptr));
474
475                         return (string []) splits.ToArray (typeof (string));
476                 }
477
478                 // This method is called at the end of the constructor of compiled
479                 // regular expression classes to do internal initialization.
480                 protected void InitializeReferences ()
481                 {
482                         if (refsInitialized)
483                                 throw new NotSupportedException ("This operation is only allowed once per object.");
484
485                         refsInitialized = true;
486
487                         // Compile pattern that results in performance loss as existing
488                         // CIL code is ignored but provides support for regular
489                         // expressions compiled to assemblies.
490                         Init ();
491                 }
492
493                 protected bool UseOptionC ()
494                 {
495                         return ((roptions & RegexOptions.Compiled) != 0);
496                 }
497
498                 protected bool UseOptionR ()
499                 {
500                         return ((roptions & RegexOptions.RightToLeft) != 0);
501                 }
502
503                 // object methods
504                 
505                 public override string ToString ()
506                 {
507                         return pattern;
508                 }
509
510                 // ISerializable interface
511                 void ISerializable.GetObjectData (SerializationInfo info, StreamingContext context)
512                 {
513                         info.AddValue ("pattern", this.ToString (), typeof (string));
514                         info.AddValue ("options", this.Options, typeof (RegexOptions));
515                 }
516
517                 // internal
518
519                 internal int GroupCount {
520                         get { return group_count; }
521                 }
522
523                 // private
524
525                 private IMachine CreateMachine ()
526                 {
527                         return machineFactory.NewInstance ();
528                 }
529
530                 private IMachineFactory machineFactory;
531                 private IDictionary mapping;
532                 private int group_count;
533                 private bool refsInitialized;
534
535                 
536                 // protected members
537
538                 protected internal string pattern;
539                 protected internal RegexOptions roptions;
540                 
541                 // MS undocumented members
542                 [MonoTODO]
543                 protected internal System.Collections.Hashtable capnames;
544                 [MonoTODO]
545                 protected internal System.Collections.Hashtable caps;
546                 [MonoTODO]
547                 protected internal int capsize;
548                 [MonoTODO]
549                 protected internal string [] capslist;
550                 [MonoTODO]
551                 protected internal RegexRunnerFactory factory;
552         }
553 }