Add support to build build with Mono.
[mono.git] / mcs / class / referencesource / System / regex / system / text / regularexpressions / RegexMatch.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="RegexMatch.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 // Match is the result class for a regex search.
8 // It returns the location, length, and substring for
9 // the entire match as well as every captured group.
10
11 // Match is also used during the search to keep track of each capture for each group.  This is
12 // done using the "_matches" array.  _matches[x] represents an array of the captures for group x.  
13 // This array consists of start and length pairs, and may have empty entries at the end.  _matchcount[x] 
14 // stores how many captures a group has.  Note that _matchcount[x]*2 is the length of all the valid
15 // values in _matches.  _matchcount[x]*2-2 is the Start of the last capture, and _matchcount[x]*2-1 is the
16 // Length of the last capture
17 //
18 // For example, if group 2 has one capture starting at position 4 with length 6, 
19 // _matchcount[2] == 1
20 // _matches[2][0] == 4
21 // _matches[2][1] == 6
22 //
23 // Values in the _matches array can also be negative.  This happens when using the balanced match 
24 // construct, "(?<start-end>...)".  When the "end" group matches, a capture is added for both the "start" 
25 // and "end" groups.  The capture added for "start" receives the negative values, and these values point to 
26 // the next capture to be balanced.  They do NOT point to the capture that "end" just balanced out.  The negative 
27 // values are indices into the _matches array transformed by the formula -3-x.  This formula also untransforms. 
28 // 
29
30 namespace System.Text.RegularExpressions {
31
32     using System.Collections;
33     using System.Collections.Generic;
34     using System.Diagnostics;
35     using System.Security.Permissions;
36     using System.Globalization;
37
38     /// <devdoc>
39     ///    <para>
40     ///       Represents 
41     ///          the results from a single regular expression match.
42     ///       </para>
43     ///    </devdoc>
44 #if !SILVERLIGHT
45     [ Serializable() ] 
46 #endif
47     public class Match : Group {
48         internal static Match _empty = new Match(null, 1, String.Empty, 0, 0, 0);
49         internal GroupCollection _groupcoll;
50         
51         // input to the match
52         internal Regex               _regex;
53         internal int                 _textbeg;
54         internal int                 _textpos;
55         internal int                 _textend;
56         internal int                 _textstart;
57
58         // output from the match
59         internal int[][]             _matches;
60         internal int[]               _matchcount;
61         internal bool                _balancing;        // whether we've done any balancing with this match.  If we
62                                                         // have done balancing, we'll need to do extra work in Tidy().
63
64         /// <devdoc>
65         ///    <para>
66         ///       Returns an empty Match object.
67         ///    </para>
68         /// </devdoc>
69         public static Match Empty {
70             get {
71                 return _empty;
72             }
73         }
74
75         /*
76          * Nonpublic constructor
77          */
78         internal Match(Regex regex, int capcount, String text, int begpos, int len, int startpos)
79
80         : base(text, new int[2], 0) {
81
82             _regex      = regex;
83             _matchcount = new int[capcount];
84
85             _matches    = new int[capcount][];
86             _matches[0] = _caps;
87             _textbeg    = begpos;
88             _textend    = begpos + len;
89             _textstart  = startpos;
90             _balancing  = false;
91
92             // No need for an exception here.  This is only called internally, so we'll use an Assert instead
93             System.Diagnostics.Debug.Assert(!(_textbeg < 0 || _textstart < _textbeg || _textend < _textstart || _text.Length < _textend), 
94                                             "The parameters are out of range.");
95             
96         }
97
98         /*
99          * Nonpublic set-text method
100          */
101         internal virtual void Reset(Regex regex, String text, int textbeg, int textend, int textstart) {
102             _regex = regex;
103             _text = text;
104             _textbeg = textbeg;
105             _textend = textend;
106             _textstart = textstart;
107
108             for (int i = 0; i < _matchcount.Length; i++) {
109                 _matchcount[i] = 0;
110             }
111
112             _balancing = false;
113         }
114
115         /// <devdoc>
116         ///    <para>[To be supplied.]</para>
117         /// </devdoc>
118         public virtual GroupCollection Groups {
119             get {
120                 if (_groupcoll == null)
121                     _groupcoll = new GroupCollection(this, null);
122
123                 return _groupcoll;
124             }
125         }
126
127         /*
128          * Returns the next match
129          */
130         /// <devdoc>
131         ///    <para>Returns a new Match with the results for the next match, starting
132         ///       at the position at which the last match ended (at the character beyond the last
133         ///       matched character).</para>
134         /// </devdoc>
135         public Match NextMatch() {
136             if (_regex == null)
137                 return this;
138
139             return _regex.Run(false, _length, _text, _textbeg, _textend - _textbeg, _textpos);
140         }
141
142
143         /*
144          * Return the result string (using the replacement pattern)
145          */
146         /// <devdoc>
147         ///    <para>
148         ///       Returns the expansion of the passed replacement pattern. For
149         ///       example, if the replacement pattern is ?$1$2?, Result returns the concatenation
150         ///       of Group(1).ToString() and Group(2).ToString().
151         ///    </para>
152         /// </devdoc>
153         public virtual String Result(String replacement) {
154             RegexReplacement repl;
155
156             if (replacement == null)
157                 throw new ArgumentNullException("replacement");
158
159             if (_regex == null)
160                 throw new NotSupportedException(SR.GetString(SR.NoResultOnFailed));
161
162             repl = (RegexReplacement)_regex.replref.Get();
163
164             if (repl == null || !repl.Pattern.Equals(replacement)) {
165                 repl = RegexParser.ParseReplacement(replacement, _regex.caps, _regex.capsize, _regex.capnames, _regex.roptions);
166                 _regex.replref.Cache(repl);
167             }
168
169             return repl.Replacement(this);
170         }
171
172         /*
173          * Used by the replacement code
174          */
175         internal virtual String GroupToStringImpl(int groupnum) {
176             int c = _matchcount[groupnum];
177             if (c == 0)
178                 return String.Empty;
179
180             int [] matches = _matches[groupnum];
181
182             return _text.Substring(matches[(c - 1) * 2], matches[(c * 2) - 1]);
183         }
184
185         /*
186          * Used by the replacement code
187          */
188         internal String LastGroupToStringImpl() {
189             return GroupToStringImpl(_matchcount.Length - 1);
190         }
191
192
193         /*
194          * Convert to a thread-safe object by precomputing cache contents
195          */
196         /// <devdoc>
197         ///    <para>
198         ///       Returns a Match instance equivalent to the one supplied that is safe to share
199         ///       between multiple threads.
200         ///    </para>
201         /// </devdoc>
202
203 #if !SILVERLIGHT
204 #if !DISABLE_CAS_USE
205         [HostProtection(Synchronization=true)]
206 #endif
207         static public Match Synchronized(Match inner) {
208 #else
209         static internal Match Synchronized(Match inner) {
210 #endif
211             if (inner == null)
212                 throw new ArgumentNullException("inner");
213
214             int numgroups = inner._matchcount.Length;
215
216             // Populate all groups by looking at each one
217             for (int i = 0; i < numgroups; i++) {
218                 Group group = inner.Groups[i];
219
220                 // Depends on the fact that Group.Synchronized just
221                 // operates on and returns the same instance
222                 System.Text.RegularExpressions.Group.Synchronized(group);
223             }
224
225             return inner;
226         }
227
228         /*
229          * Nonpublic builder: add a capture to the group specified by "cap"
230          */
231         internal virtual void AddMatch(int cap, int start, int len) {
232             int capcount;
233         
234             if (_matches[cap] == null)
235                 _matches[cap] = new int[2];
236         
237             capcount = _matchcount[cap];
238         
239             if (capcount * 2 + 2 > _matches[cap].Length) {
240                 int[] oldmatches = _matches[cap];
241                 int[] newmatches = new int[capcount * 8];
242                 for (int j = 0; j < capcount * 2; j++)
243                     newmatches[j] = oldmatches[j];
244                 _matches[cap] = newmatches;
245             }
246         
247             _matches[cap][capcount * 2] = start;
248             _matches[cap][capcount * 2 + 1] = len;
249             _matchcount[cap] = capcount + 1;
250         }
251
252         /*
253          * Nonpublic builder: Add a capture to balance the specified group.  This is used by the 
254                               balanced match construct. (?<foo-foo2>...)
255
256            If there were no such thing as backtracking, this would be as simple as calling RemoveMatch(cap).
257            However, since we have backtracking, we need to keep track of everything. 
258          */
259         internal virtual void BalanceMatch(int cap) {
260             int capcount;
261             int target;
262
263             _balancing = true;
264
265             // we'll look at the last capture first
266             capcount = _matchcount[cap];
267             target = capcount * 2 - 2;
268
269             // first see if it is negative, and therefore is a reference to the next available
270             // capture group for balancing.  If it is, we'll reset target to point to that capture.
271             if (_matches[cap][target] < 0)
272                 target = -3 - _matches[cap][target];
273
274             // move back to the previous capture
275             target -= 2;
276
277             // if the previous capture is a reference, just copy that reference to the end.  Otherwise, point to it. 
278             if (target >= 0 && _matches[cap][target] < 0)
279                 AddMatch(cap, _matches[cap][target], _matches[cap][target+1]);
280             else
281                 AddMatch(cap, -3 - target, -4 - target /* == -3 - (target + 1) */ );
282
283         }
284
285         /*
286          * Nonpublic builder: removes a group match by capnum
287          */
288         internal virtual void RemoveMatch(int cap) {
289             _matchcount[cap]--;
290         }
291
292         /*
293          * Nonpublic: tells if a group was matched by capnum
294          */
295         internal virtual bool IsMatched(int cap) {
296             return cap < _matchcount.Length && _matchcount[cap] > 0 && _matches[cap][_matchcount[cap] * 2 - 1] != (-3 + 1);
297         }
298
299         /*
300          * Nonpublic: returns the index of the last specified matched group by capnum
301          */
302         internal virtual int MatchIndex(int cap) {
303             int i = _matches[cap][_matchcount[cap] * 2 - 2];
304             if (i >= 0)
305                 return i;
306
307             return _matches[cap][-3 - i];
308         }
309
310         /*
311          * Nonpublic: returns the length of the last specified matched group by capnum
312          */
313         internal virtual int MatchLength(int cap) {
314             int i = _matches[cap][_matchcount[cap] * 2 - 1];
315             if (i >= 0)
316                 return i;
317
318             return _matches[cap][-3 - i];
319         }
320
321         /*
322          * Nonpublic: tidy the match so that it can be used as an immutable result
323          */
324         internal virtual void Tidy(int textpos) {
325             int[] interval;
326
327             interval  = _matches[0];
328             _index    = interval[0];
329             _length   = interval[1];
330             _textpos  = textpos;
331             _capcount = _matchcount[0];
332
333             if (_balancing) {
334                 // The idea here is that we want to compact all of our unbalanced captures.  To do that we
335                 // use j basically as a count of how many unbalanced captures we have at any given time 
336                 // (really j is an index, but j/2 is the count).  First we skip past all of the real captures
337                 // until we find a balance captures.  Then we check each subsequent entry.  If it's a balance
338                 // capture (it's negative), we decrement j.  If it's a real capture, we increment j and copy 
339                 // it down to the last free position. 
340                 for (int cap = 0; cap < _matchcount.Length; cap++) {
341                     int limit;
342                     int[] matcharray;
343
344                     limit = _matchcount[cap] * 2;
345                     matcharray = _matches[cap];
346
347                     int i = 0;
348                     int j;
349
350                     for (i = 0; i < limit; i++) {
351                         if (matcharray[i] < 0)
352                             break;
353                     }
354
355                     for (j = i; i < limit; i++) {
356                         if (matcharray[i] < 0) {
357                             // skip negative values
358                             j--;
359                         }
360                         else {
361                             // but if we find something positive (an actual capture), copy it back to the last 
362                             // unbalanced position. 
363                             if (i != j)
364                                 matcharray[j] = matcharray[i];
365                             j++;
366                         }
367                     }
368
369                     _matchcount[cap] = j / 2;
370                 }
371
372                 _balancing = false;
373             }
374         }
375
376 #if DBG
377         /// <internalonly/>
378         /// <devdoc>
379         /// </devdoc>
380         public bool Debug {
381             get {
382                 if (_regex == null)
383                     return false; 
384
385                 return _regex.Debug;
386             }
387         }
388
389         /// <internalonly/>
390         /// <devdoc>
391         /// </devdoc>
392         internal virtual void Dump() {
393             int i,j;
394
395             for (i = 0; i < _matchcount.Length; i++) {
396                 System.Diagnostics.Debug.WriteLine("Capnum " + i.ToString(CultureInfo.InvariantCulture) + ":");
397
398                 for (j = 0; j < _matchcount[i]; j++) {
399                     String text = "";
400
401                     if (_matches[i][j * 2] >= 0)
402                         text = _text.Substring(_matches[i][j * 2], _matches[i][j * 2 + 1]);
403
404                     System.Diagnostics.Debug.WriteLine("  (" + _matches[i][j * 2].ToString(CultureInfo.InvariantCulture) + "," + _matches[i][j * 2 + 1].ToString(CultureInfo.InvariantCulture) + ") " + text);
405                 }
406             }
407         }
408 #endif
409     }
410
411
412     /*
413      * MatchSparse is for handling the case where slots are
414      * sparsely arranged (e.g., if somebody says use slot 100000)
415      */
416     internal class MatchSparse : Match {
417         // the lookup hashtable
418 #if SILVERLIGHT
419         new internal Dictionary<Int32, Int32> _caps;
420 #else
421         new internal Hashtable _caps;
422 #endif
423
424         /*
425          * Nonpublic constructor
426          */
427 #if SILVERLIGHT
428         internal MatchSparse(Regex regex, Dictionary<Int32, Int32> caps, int capcount,
429 #else
430         internal MatchSparse(Regex regex, Hashtable caps, int capcount,
431 #endif
432                              String text, int begpos, int len, int startpos)
433
434         : base(regex, capcount, text, begpos, len, startpos) {
435
436             _caps = caps;
437         }
438
439         public override GroupCollection Groups {
440             get {
441                 if (_groupcoll == null)
442                     _groupcoll = new GroupCollection(this, _caps);
443
444                 return _groupcoll;
445             }
446         }
447
448 #if DBG
449         internal override void Dump() {
450             if (_caps != null) {
451 #if SILVERLIGHT
452                 IEnumerator<Int32> e = _caps.Keys.GetEnumerator();
453 #else
454                 IEnumerator e = _caps.Keys.GetEnumerator();
455 #endif
456                 while (e.MoveNext()) {
457                     System.Diagnostics.Debug.WriteLine("Slot " + e.Current.ToString() + " -> " + _caps[e.Current].ToString());
458                 }
459             }
460
461             base.Dump();
462         }
463 #endif
464
465     }
466
467
468 }