Merge pull request #3386 from alexanderkyte/nunit_lite_return_status
[mono.git] / mcs / class / referencesource / System.Web / Util / Wildcard.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="Wildcard.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 /*
8  * Wildcard
9  * 
10  * wildcard wrappers for Regex
11  *
12  * (1) Wildcard does straight string wildcarding with no path separator awareness
13  * (2) WildcardUrl recognizes that forward / slashes are special and can't match * or ?
14  * (3) WildcardDos recognizes that backward \ and : are special and can't match * or ?
15  * 
16  * Copyright (c) 1999, Microsoft Corporation
17  */
18 namespace System.Web.Util {
19     using System.Runtime.Serialization.Formatters;
20     using System.Text.RegularExpressions;
21
22     /*
23      * Wildcard
24      *
25      * Wildcard patterns have three metacharacters:
26      *
27      * A ? is equivalent to .
28      * A * is equivalent to .*
29      * A , is equivalent to |
30      *
31      * Note that by each alternative is surrounded by \A...\z to anchor
32      * at the edges of the string.
33      */
34     internal class Wildcard {
35 #if NOT_USED
36         internal /*public*/ Wildcard(String pattern) : this (pattern, false) {
37         }
38 #endif
39
40         internal /*public*/ Wildcard(String pattern, bool caseInsensitive) {
41             _pattern = pattern;
42             _caseInsensitive = caseInsensitive;
43         }
44
45         internal String _pattern;
46         internal bool _caseInsensitive;
47         internal Regex _regex;
48
49         protected static Regex metaRegex = new Regex("[\\+\\{\\\\\\[\\|\\(\\)\\.\\^\\$]");
50         protected static Regex questRegex = new Regex("\\?");
51         protected static Regex starRegex = new Regex("\\*");
52         protected static Regex commaRegex = new Regex(",");
53         protected static Regex slashRegex = new Regex("(?=/)");
54         protected static Regex backslashRegex = new Regex("(?=[\\\\:])");
55
56         /*
57          * IsMatch returns true if the input is an exact match for the
58          * wildcard pattern.
59          */
60         internal /*public*/ bool IsMatch(String input) {
61             EnsureRegex();
62
63             bool result =  _regex.IsMatch(input);
64
65             return result;
66         }
67 #if DONT_COMPILE
68         internal /*public*/ String Pattern {
69             get {
70                 return _pattern;
71             }
72         }
73 #endif
74         /*
75          * Builds the matching regex when needed
76          */
77         protected void EnsureRegex() {
78             // threadsafe without protection because of gc
79
80             if (_regex != null)
81                 return;
82
83             _regex = RegexFromWildcard(_pattern, _caseInsensitive);
84         }
85
86         /*
87          * Basic wildcard -> Regex conversion, no slashes
88          */
89         protected virtual Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
90             RegexOptions options = RegexOptions.None;
91
92             // match right-to-left (for speed) if the pattern starts with a *
93
94             if (pattern.Length > 0 && pattern[0] == '*')
95                 options = RegexOptions.RightToLeft | RegexOptions.Singleline;
96             else
97                 options = RegexOptions.Singleline;
98
99             // case insensitivity
100
101             if (caseInsensitive)
102                 options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
103
104             // Remove regex metacharacters
105
106             pattern = metaRegex.Replace(pattern, "\\$0");
107
108             // Replace wildcard metacharacters with regex codes
109
110             pattern = questRegex.Replace(pattern, ".");
111             pattern = starRegex.Replace(pattern, ".*");
112             pattern = commaRegex.Replace(pattern, "\\z|\\A");
113
114             // anchor the pattern at beginning and end, and return the regex
115
116             return new Regex("\\A" + pattern + "\\z", options);
117         }
118     }
119
120     abstract internal class WildcardPath : Wildcard {
121 #if NOT_USED        
122         internal /*public*/ WildcardPath(String pattern) : base(pattern) {
123         }
124
125         private Regex[][] _dirs;
126 #endif
127
128         internal /*public*/ WildcardPath(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
129         }
130
131         private Regex _suffix;
132
133         /*
134          * IsSuffix returns true if a suffix of the input is an exact
135          * match for the wildcard pattern.
136          */
137         internal /*public*/ bool IsSuffix(String input) {
138             EnsureSuffix();
139             return _suffix.IsMatch(input);
140         }
141
142 #if NOT_USED        
143         /*
144          * AllowPrefix returns true if the input is an exact match for
145          * a prefix-directory of the wildcard pattern (i.e., if it
146          * is possible to match the wildcard pattern by adding
147          * more subdirectories or a filename at the end of the path).
148          */
149         internal /*public*/ bool AllowPrefix(String prefix) {
150             String[] dirs = SplitDirs(prefix);
151
152             EnsureDirs();
153
154             for (int i = 0; i < _dirs.Length; i++) {
155                 // pattern is shorter than prefix: reject
156                 if (_dirs[i].Length < dirs.Length)
157                     goto NextAlt;
158
159                 for (int j = 0; j < dirs.Length; j++) {
160                     // the jth directory doesn't match; path is not a prefix
161                     if (!_dirs[i][j].IsMatch(dirs[j]))
162                         goto NextAlt;
163                 }
164
165                 // one alternative passed: we pass.
166
167                 return true;
168
169                 NextAlt: 
170                 ;
171             }
172
173             return false;
174         }
175
176         /*
177          * Builds the matching regex array when needed
178          */
179         protected void EnsureDirs() {
180             // threadsafe without protection because of gc
181
182             if (_dirs != null)
183                 return;
184
185             _dirs = DirsFromWildcard(_pattern);
186         }
187 #endif
188
189         /*
190          * Builds the matching regex when needed
191          */
192         protected void EnsureSuffix() {
193             // threadsafe without protection because of gc
194
195             if (_suffix != null)
196                 return;
197
198             _suffix = SuffixFromWildcard(_pattern, _caseInsensitive);
199         }
200
201
202         /*
203          * Specialize for forward-slash and backward-slash cases
204          */
205         protected abstract Regex SuffixFromWildcard(String pattern, bool caseInsensitive);
206         protected abstract Regex[][] DirsFromWildcard(String pattern);
207         protected abstract String[] SplitDirs(String input);
208     }
209
210     /*
211      * WildcardUrl
212      *
213      * The twist is that * and ? cannot match forward slashes,
214      * and we can do an exact suffix match that starts after
215      * any /, and we can also do a prefix prune.
216      */
217     internal class WildcardUrl : WildcardPath {
218 #if NOT_USED
219         internal /*public*/ WildcardUrl(String pattern) : base(pattern) {
220         }
221 #endif
222         internal /*public*/ WildcardUrl(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
223         }
224
225         protected override String[] SplitDirs(String input) {
226             return slashRegex.Split(input);
227         }
228
229         protected override Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
230             RegexOptions options;
231
232             // match right-to-left (for speed) if the pattern starts with a *
233
234             if (pattern.Length > 0 && pattern[0] == '*')
235                 options = RegexOptions.RightToLeft;
236             else
237                 options = RegexOptions.None;
238
239             // case insensitivity
240
241             if (caseInsensitive)
242                 options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
243
244             // Remove regex metacharacters
245
246             pattern = metaRegex.Replace(pattern, "\\$0");
247
248             // Replace wildcard metacharacters with regex codes
249
250             pattern = questRegex.Replace(pattern, "[^/]");
251             pattern = starRegex.Replace(pattern, "[^/]*");
252             pattern = commaRegex.Replace(pattern, "\\z|\\A");
253
254             // anchor the pattern at beginning and end, and return the regex
255
256             return new Regex("\\A" + pattern + "\\z", options);
257         }
258
259         protected override Regex SuffixFromWildcard(String pattern, bool caseInsensitive) {
260             RegexOptions options;
261
262             // match right-to-left (for speed)
263
264             options = RegexOptions.RightToLeft;
265
266             // case insensitivity
267
268             if (caseInsensitive)
269                 options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
270
271             // Remove regex metacharacters
272
273             pattern = metaRegex.Replace(pattern, "\\$0");
274
275             // Replace wildcard metacharacters with regex codes
276
277             pattern = questRegex.Replace(pattern, "[^/]");
278             pattern = starRegex.Replace(pattern, "[^/]*");
279             pattern = commaRegex.Replace(pattern, "\\z|(?:\\A|(?<=/))");
280
281             // anchor the pattern at beginning and end, and return the regex
282
283             return new Regex("(?:\\A|(?<=/))" + pattern + "\\z", options);
284         }
285
286         protected override Regex[][] DirsFromWildcard(String pattern) {
287             String[] alts = commaRegex.Split(pattern);
288             Regex[][] dirs = new Regex[alts.Length][];
289
290             for (int i = 0; i < alts.Length; i++) {
291                 String[] dirpats = slashRegex.Split(alts[i]);
292
293                 Regex[] dirregex = new Regex[dirpats.Length];
294
295                 if (alts.Length == 1 && dirpats.Length == 1) {
296                     // common case: no commas, no slashes: dir regex is same as top regex.
297
298                     EnsureRegex();
299                     dirregex[0] = _regex;
300                 }
301                 else {
302                     for (int j = 0; j < dirpats.Length; j++) {
303                         dirregex[j] = RegexFromWildcard(dirpats[j], _caseInsensitive);
304                     }
305                 }
306
307                 dirs[i] = dirregex;
308             }
309
310             return dirs;
311         }
312     }
313 }