Fix bugs in sizing TableLayoutPanel (Xamarin bug 18638)
[mono.git] / mcs / class / System / System.Text.RegularExpressions / replace.cs
1 //
2 // assembly:    System
3 // namespace:   System.Text.RegularExpressions
4 // file:        replace.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
34 using Parser = System.Text.RegularExpressions.Syntax.Parser;
35
36 namespace System.Text.RegularExpressions {
37
38         class ReplacementEvaluator {
39                 public static string Evaluate (string replacement, Match match)
40                 {
41                         ReplacementEvaluator ev = new ReplacementEvaluator (match.Regex, replacement);
42                         return ev.Evaluate (match);
43                 }
44
45                 public ReplacementEvaluator (Regex regex, string replacement)
46                 {
47                         this.regex = regex;
48                         this.replacement = replacement;
49                         this.pieces = null;
50                         this.n_pieces = 0;
51                         Compile ();
52                 }
53
54                 public string Evaluate (Match match) 
55                 {
56                         if (n_pieces == 0)
57                                 return replacement;
58                         StringBuilder sb = new StringBuilder ();
59                         EvaluateAppend (match, sb);
60                         return sb.ToString ();
61                 }
62
63                 public void EvaluateAppend (Match match, StringBuilder sb)
64                 {
65                         if (n_pieces == 0) {
66                                 sb.Append (replacement);
67                                 return;
68                         }
69
70                         int i = 0;
71                         while (i < n_pieces) {
72                                 int k = pieces [i++];
73                                 if (k >= 0) {
74                                         int count = pieces [i++];
75                                         sb.Append (replacement, k, count);
76                                 } else if (k < -3) {
77                                         Group group = match.Groups [-(k + 4)];
78                                         sb.Append (group.Text, group.Index, group.Length);
79                                 } else if (k == -1) {
80                                         sb.Append (match.Text);
81                                 } else if (k == -2) {
82                                         sb.Append (match.Text, 0, match.Index);
83                                 } else { // k == -3
84                                         int matchend = match.Index + match.Length;
85                                         sb.Append (match.Text, matchend, match.Text.Length - matchend);
86                                 } 
87                         }
88                 }
89
90                 public bool NeedsGroupsOrCaptures {
91                         get {
92                                 if (n_pieces == 0)
93                                         return false;
94                                 else
95                                         return true;
96                         }
97                 }
98
99                 void Ensure (int size)
100                 {
101                         int new_size;
102                         if (pieces == null) {
103                                 new_size = 4;
104                                 if (new_size < size)
105                                         new_size = size;
106                                 pieces = new int [new_size];
107                         } else if (size >= pieces.Length) {
108                                 new_size = pieces.Length + (pieces.Length >> 1);
109                                 if (new_size < size)
110                                         new_size = size;
111                                 int [] new_pieces = new int [new_size];
112                                 Array.Copy (pieces, new_pieces, n_pieces);
113                                 pieces = new_pieces;
114                         }
115                 }
116
117                 void AddFromReplacement (int start, int end)
118                 {
119                         if (start == end)
120                                 return;
121                         Ensure (n_pieces + 2);
122                         pieces [n_pieces++] = start;
123                         pieces [n_pieces++] = end - start;
124                 }
125
126                 void AddInt (int i)
127                 {
128                         Ensure (n_pieces + 1);
129                         pieces [n_pieces++] = i;
130                 }
131
132                 // private
133                 private void Compile ()
134                 {
135                         int anchor = 0, ptr = 0, saveptr;
136                         char c;
137                         while (ptr < replacement.Length) {
138                                 c = replacement [ptr++];
139
140                                 if (c != '$')
141                                         continue;
142
143                                 // If the '$' was the last character, just emit it as is
144                                 if (ptr == replacement.Length)
145                                         break;
146
147                                 // If we saw a '$$'
148                                 if (replacement [ptr] == '$') {
149                                         // Everthing from 'anchor' upto and including the first '$' is copied from the replacement string
150                                         AddFromReplacement (anchor, ptr);
151                                         // skip over the second '$'.
152                                         anchor = ++ptr;
153                                         continue;
154                                 }
155
156                                 saveptr = ptr - 1;
157
158                                 int from_match = CompileTerm (ref ptr);
159
160                                 // We couldn't recognize the term following the '$'.  Just treat it as a literal.
161                                 // 'ptr' has already been advanced, no need to rewind it back
162                                 if (from_match >= 0)
163                                         continue;
164
165                                 AddFromReplacement (anchor, saveptr);
166                                 AddInt (from_match);
167                                 anchor = ptr;
168                         }
169
170                         // If we never needed to advance anchor, it means the result is the whole replacement string.
171                         // We optimize that case by never allocating the pieces array.
172                         if (anchor != 0)
173                                 AddFromReplacement (anchor, ptr);
174                 }
175
176                 private int CompileTerm (ref int ptr)
177                 {
178                         char c = replacement [ptr];
179
180                         if (Char.IsDigit (c)) {         // numbered group
181                                 int n = Parser.ParseDecimal (replacement, ref ptr);
182                                 if (n < 0 || n > regex.GroupCount)
183                                         return 0;
184                                 
185                                 return -n - 4;
186                         }
187                         
188                         ++ ptr;
189
190                         switch (c) {
191                         case '{': {                     // named group
192                                 string name;
193                                 int n = -1;
194
195                                 try {
196                                         // The parser is written such that there are few explicit range checks
197                                         // and depends on 'IndexOutOfRangeException' being thrown.
198
199                                         if (Char.IsDigit (replacement [ptr])) {
200                                                 n = Parser.ParseDecimal (replacement, ref ptr);
201                                                 name = "";
202                                         } else {
203                                                 name = Parser.ParseName (replacement, ref ptr);
204                                         }
205                                 } catch (IndexOutOfRangeException) {
206                                         ptr = replacement.Length;
207                                         return 0;
208                                 }
209
210                                 if (ptr == replacement.Length || replacement[ptr] != '}' || name == null)
211                                         return 0;
212                                 ++ptr;                  // Swallow the '}'
213
214                                 if (name != "")
215                                         n = regex.GroupNumberFromName (name);
216
217                                 if (n < 0 || n > regex.GroupCount)
218                                         return 0;
219
220                                 return -n - 4;
221                         }
222
223                         case '&':                       // entire match.  Value should be same as $0
224                                 return -4;
225
226                         case '`':                       // text before match
227                                 return -2;
228
229                         case '\'':                      // text after match
230                                 return -3;
231
232                         case '+':                       // last group
233                                 return -regex.GroupCount - 4;
234
235                         case '_':                       // entire text
236                                 return -1;
237
238                         default:
239                                 return 0;
240                         }
241                 }
242
243                 private Regex regex;
244                 int n_pieces;
245                 private int [] pieces;
246                 string replacement;
247         }
248 }