File: Util\Wildcard.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="Wildcard.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * Wildcard
 * 
 * wildcard wrappers for Regex
 *
 * (1) Wildcard does straight string wildcarding with no path separator awareness
 * (2) WildcardUrl recognizes that forward / slashes are special and can't match * or ?
 * (3) WildcardDos recognizes that backward \ and : are special and can't match * or ?
 * 
 * Copyright (c) 1999, Microsoft Corporation
 */
namespace System.Web.Util {
    using System.Runtime.Serialization.Formatters;
    using System.Text.RegularExpressions;
 
    /*
     * Wildcard
     *
     * Wildcard patterns have three metacharacters:
     *
     * A ? is equivalent to .
     * A * is equivalent to .*
     * A , is equivalent to |
     *
     * Note that by each alternative is surrounded by \A...\z to anchor
     * at the edges of the string.
     */
    internal class Wildcard {
#if NOT_USED
        internal /*public*/ Wildcard(String pattern) : this (pattern, false) {
        }
#endif
 
        internal /*public*/ Wildcard(String pattern, bool caseInsensitive) {
            _pattern = pattern;
            _caseInsensitive = caseInsensitive;
        }
 
        internal String _pattern;
        internal bool _caseInsensitive;
        internal Regex _regex;
 
        protected static Regex metaRegex = new Regex("[\\+\\{\\\\\\[\\|\\(\\)\\.\\^\\$]");
        protected static Regex questRegex = new Regex("\\?");
        protected static Regex starRegex = new Regex("\\*");
        protected static Regex commaRegex = new Regex(",");
        protected static Regex slashRegex = new Regex("(?=/)");
        protected static Regex backslashRegex = new Regex("(?=[\\\\:])");
 
        /*
         * IsMatch returns true if the input is an exact match for the
         * wildcard pattern.
         */
        internal /*public*/ bool IsMatch(String input) {
            EnsureRegex();
 
            bool result =  _regex.IsMatch(input);
 
            return result;
        }
#if DONT_COMPILE
        internal /*public*/ String Pattern {
            get {
                return _pattern;
            }
        }
#endif
        /*
         * Builds the matching regex when needed
         */
        protected void EnsureRegex() {
            // threadsafe without protection because of gc
 
            if (_regex != null)
                return;
 
            _regex = RegexFromWildcard(_pattern, _caseInsensitive);
        }
 
        /*
         * Basic wildcard -> Regex conversion, no slashes
         */
        protected virtual Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
            RegexOptions options = RegexOptions.None;
 
            // match right-to-left (for speed) if the pattern starts with a *
 
            if (pattern.Length > 0 && pattern[0] == '*')
                options = RegexOptions.RightToLeft | RegexOptions.Singleline;
            else
                options = RegexOptions.Singleline;
 
            // case insensitivity
 
            if (caseInsensitive)
                options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
 
            // Remove regex metacharacters
 
            pattern = metaRegex.Replace(pattern, "\\$0");
 
            // Replace wildcard metacharacters with regex codes
 
            pattern = questRegex.Replace(pattern, ".");
            pattern = starRegex.Replace(pattern, ".*");
            pattern = commaRegex.Replace(pattern, "\\z|\\A");
 
            // anchor the pattern at beginning and end, and return the regex
 
            return new Regex("\\A" + pattern + "\\z", options);
        }
    }
 
    abstract internal class WildcardPath : Wildcard {
#if NOT_USED        
        internal /*public*/ WildcardPath(String pattern) : base(pattern) {
        }
 
        private Regex[][] _dirs;
#endif
 
        internal /*public*/ WildcardPath(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
        }
 
        private Regex _suffix;
 
        /*
         * IsSuffix returns true if a suffix of the input is an exact
         * match for the wildcard pattern.
         */
        internal /*public*/ bool IsSuffix(String input) {
            EnsureSuffix();
            return _suffix.IsMatch(input);
        }
 
#if NOT_USED        
        /*
         * AllowPrefix returns true if the input is an exact match for
         * a prefix-directory of the wildcard pattern (i.e., if it
         * is possible to match the wildcard pattern by adding
         * more subdirectories or a filename at the end of the path).
         */
        internal /*public*/ bool AllowPrefix(String prefix) {
            String[] dirs = SplitDirs(prefix);
 
            EnsureDirs();
 
            for (int i = 0; i < _dirs.Length; i++) {
                // pattern is shorter than prefix: reject
                if (_dirs[i].Length < dirs.Length)
                    goto NextAlt;
 
                for (int j = 0; j < dirs.Length; j++) {
                    // the jth directory doesn't match; path is not a prefix
                    if (!_dirs[i][j].IsMatch(dirs[j]))
                        goto NextAlt;
                }
 
                // one alternative passed: we pass.
 
                return true;
 
                NextAlt: 
                ;
            }
 
            return false;
        }
 
        /*
         * Builds the matching regex array when needed
         */
        protected void EnsureDirs() {
            // threadsafe without protection because of gc
 
            if (_dirs != null)
                return;
 
            _dirs = DirsFromWildcard(_pattern);
        }
#endif
 
        /*
         * Builds the matching regex when needed
         */
        protected void EnsureSuffix() {
            // threadsafe without protection because of gc
 
            if (_suffix != null)
                return;
 
            _suffix = SuffixFromWildcard(_pattern, _caseInsensitive);
        }
 
 
        /*
         * Specialize for forward-slash and backward-slash cases
         */
        protected abstract Regex SuffixFromWildcard(String pattern, bool caseInsensitive);
        protected abstract Regex[][] DirsFromWildcard(String pattern);
        protected abstract String[] SplitDirs(String input);
    }
 
    /*
     * WildcardUrl
     *
     * The twist is that * and ? cannot match forward slashes,
     * and we can do an exact suffix match that starts after
     * any /, and we can also do a prefix prune.
     */
    internal class WildcardUrl : WildcardPath {
#if NOT_USED
        internal /*public*/ WildcardUrl(String pattern) : base(pattern) {
        }
#endif
        internal /*public*/ WildcardUrl(String pattern, bool caseInsensitive) : base(pattern, caseInsensitive) {
        }
 
        protected override String[] SplitDirs(String input) {
            return slashRegex.Split(input);
        }
 
        protected override Regex RegexFromWildcard(String pattern, bool caseInsensitive) {
            RegexOptions options;
 
            // match right-to-left (for speed) if the pattern starts with a *
 
            if (pattern.Length > 0 && pattern[0] == '*')
                options = RegexOptions.RightToLeft;
            else
                options = RegexOptions.None;
 
            // case insensitivity
 
            if (caseInsensitive)
                options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
 
            // Remove regex metacharacters
 
            pattern = metaRegex.Replace(pattern, "\\$0");
 
            // Replace wildcard metacharacters with regex codes
 
            pattern = questRegex.Replace(pattern, "[^/]");
            pattern = starRegex.Replace(pattern, "[^/]*");
            pattern = commaRegex.Replace(pattern, "\\z|\\A");
 
            // anchor the pattern at beginning and end, and return the regex
 
            return new Regex("\\A" + pattern + "\\z", options);
        }
 
        protected override Regex SuffixFromWildcard(String pattern, bool caseInsensitive) {
            RegexOptions options;
 
            // match right-to-left (for speed)
 
            options = RegexOptions.RightToLeft;
 
            // case insensitivity
 
            if (caseInsensitive)
                options |= RegexOptions.IgnoreCase | RegexOptions.CultureInvariant;
 
            // Remove regex metacharacters
 
            pattern = metaRegex.Replace(pattern, "\\$0");
 
            // Replace wildcard metacharacters with regex codes
 
            pattern = questRegex.Replace(pattern, "[^/]");
            pattern = starRegex.Replace(pattern, "[^/]*");
            pattern = commaRegex.Replace(pattern, "\\z|(?:\\A|(?<=/))");
 
            // anchor the pattern at beginning and end, and return the regex
 
            return new Regex("(?:\\A|(?<=/))" + pattern + "\\z", options);
        }
 
        protected override Regex[][] DirsFromWildcard(String pattern) {
            String[] alts = commaRegex.Split(pattern);
            Regex[][] dirs = new Regex[alts.Length][];
 
            for (int i = 0; i < alts.Length; i++) {
                String[] dirpats = slashRegex.Split(alts[i]);
 
                Regex[] dirregex = new Regex[dirpats.Length];
 
                if (alts.Length == 1 && dirpats.Length == 1) {
                    // common case: no commas, no slashes: dir regex is same as top regex.
 
                    EnsureRegex();
                    dirregex[0] = _regex;
                }
                else {
                    for (int j = 0; j < dirpats.Length; j++) {
                        dirregex[j] = RegexFromWildcard(dirpats[j], _caseInsensitive);
                    }
                }
 
                dirs[i] = dirregex;
            }
 
            return dirs;
        }
    }
}