/// <summary> /// Normalize the given path string, returning a regular expression. /// /// An empty array can be passed in for the keys, which will hold the /// placeholder key descriptions. For example, using `/user/:id`, `keys` will /// contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. /// </summary> /// <param name="path">string, Regex, or array of strings</param> /// <param name="keys"></param> /// <param name="options"></param> /// <returns></returns> public static Regex PathToRegex(object path, ref List <Token> keys, PathToRegexOptions options = null) { if (path is Regex) { return(RegexToRegex((Regex)path, ref keys)); } if (path is IEnumerable <string> ) { return(ArrayToRegex((IEnumerable <string>)path, ref keys, options)); } return(StringToRegex((string)path, ref keys, options)); }
/** * Parse a string for the raw tokens. * * @param {string} str * @param {Object=} options * @return {!Array} */ static List <Token> Parse(string str, PathToRegexOptions options = null) { options = options ?? new PathToRegexOptions(); var tokens = new List <Token>(); var key = 0; var index = 0; var path = ""; var pathEscaped = false; var res = PATH_REGEX.Matches(str); foreach (Match match in res) { var m = match.Value; var escaped = match.Groups[1]; var offset = match.Index; path += str.Substring(index, offset - index); index = offset + m.Length; // Ignore already escaped sequences. if (escaped.Captures.Count > 0) { path += escaped.Value; pathEscaped = true; continue; } var prev = ""; var name = match.Groups[2].Value; var capture = match.Groups[3].Value; var group = match.Groups[4].Value; var modifier = match.Groups[5].Value; if (!pathEscaped && path.Length > 0) { var k = path.Length - 1; var c = path[k]; var matches = options.Whitelist != null?options.Whitelist.Contains(c) : true; if (matches) { prev = c.ToString(); path = path.Substring(0, k); } } // Push the current path onto the tokens. if (!string.IsNullOrEmpty(path)) { tokens.Add(new Token { Raw = path, }); path = ""; pathEscaped = false; } var repeat = modifier == "+" || modifier == "*"; var optional = modifier == "?" || modifier == "*"; var pattern = !string.IsNullOrEmpty(capture) ? capture : group; var delimiter = !string.IsNullOrEmpty(prev) ? prev[0] : options.Delimiter; tokens.Add(new Token { Index = string.IsNullOrEmpty(name) ? key++ : (int?)null, Name = !string.IsNullOrEmpty(name) ? name : null, Prefix = prev, Delimiter = delimiter, Optional = optional, Repeat = repeat, Pattern = !string.IsNullOrEmpty(pattern) ? EscapeGroup(pattern) : ("[^" + EscapeGroup(delimiter == options.Delimiter ? "" + delimiter : ("" + delimiter + options.Delimiter)) + "]+?"), }); } // Push any remaining characters. if (!string.IsNullOrEmpty(path) || index < str.Length) { tokens.Add(new Token { Raw = path + str.Substring(index), }); } return(tokens); }
/// <summary> /// Create a path regex from string input. /// </summary> /// <param name="path"></param> /// <param name="keys"></param> /// <param name="options"></param> /// <returns></returns> public static Regex StringToRegex(string path, ref List <Token> keys, PathToRegexOptions options = null) { return(TokensToRegex(Parse(path, options), ref keys, options)); }
/// <summary> /// Transform an array into a regex. /// </summary> /// <param name="path"></param> /// <param name="keys"></param> /// <param name="options"></param> /// <returns></returns> public static Regex ArrayToRegex(IEnumerable <string> path, ref List <Token> keys, PathToRegexOptions options = null) { var parts = new List <string>(); var i = 0; foreach (var part in path) { parts.Add(PathToRegex(part, ref keys, options).ToString()); i++; } return(new Regex("(?:" + string.Join("|", parts) + ")", RegexOptions.ECMAScript | (options.Sensitive ? RegexOptions.None : RegexOptions.IgnoreCase))); }
/// <summary> /// Expose a function for taking tokens and returning a Regex. /// </summary> /// <param name="tokens"></param> /// <param name="keys"></param> /// <param name="sensitive"></param> /// <param name="strict"></param> /// <param name="start"></param> /// <param name="end"></param> /// <param name="delimiter"></param> /// <param name="endsWithChars"></param> /// <returns></returns> public static Regex TokensToRegex(List <Token> tokens, ref List <Token> keys, PathToRegexOptions options = null) { options = options ?? new PathToRegexOptions(); var endsWith = string.Join("|", (options.EndsWith == null ? new char[] { } : options.EndsWith) .Select(x => EscapeString("" + x)) .Concat(new string[] { "$" })); var route = options.Start ? "^" : ""; // Iterate over the tokens and create our regex string. for (var i = 0; i < tokens.Count; i++) { var token = tokens[i]; if (token.Raw != null) { route += EscapeString(token.Raw); } else { var capture = token.Repeat ? "(?:" + token.Pattern + ")(?:" + EscapeString(token.Delimiter?.ToString()) + "(?:" + token.Pattern + "))*" : token.Pattern; if (keys != null) { keys.Add(token); } if (token.Optional) { if (token.Prefix == null) { route += "(" + capture + ")?"; } else { route += "(?:" + EscapeString(token.Prefix) + "(" + capture + "))?"; } } else { route += EscapeString(token.Prefix) + "(" + capture + ")"; } } } if (options.End) { if (!options.Strict) { route += "(?:" + EscapeString("" + options.Delimiter) + ")?"; } route += endsWith == "$" ? "$" : "(?=" + endsWith + ")"; } else { var endToken = tokens[tokens.Count - 1]; var isEndDelimited = endToken.Raw != null ? endToken.Raw.EndsWith("" + options.Delimiter) : endToken == null; if (!options.Strict) { route += "(?:" + EscapeString("" + options.Delimiter) + "(?=" + endsWith + "))?"; } if (!isEndDelimited) { route += "(?=" + EscapeString("" + options.Delimiter) + "|" + endsWith + ")"; } } return(new Regex(route, RegexOptions.ECMAScript | (options.Sensitive ? RegexOptions.None : RegexOptions.IgnoreCase))); }