/// <summary>
        /// Factory method that constructs a <see cref="BindingTemplateSource"/> from an input binding template pattern.
        /// </summary>
        /// <remarks>
        /// A template string may contain parameters embraced with curly brackets, which get replaced
        /// with values later when the template is bound.
        /// </remarks>
        /// <example>
        /// Below is a minimal template that illustrates a few basics:
        /// {p1}-p2/{{2014}}/folder/{name}.{ext}
        /// </example>
        /// <param name="pattern">A binding template pattern string in a supported format (see remarks).
        /// </param>
        /// <param name="ignoreCase">True if matches should be case insensitive.</param>
        /// <returns>An instance of <see cref="BindingTemplateSource"/> for the specified template pattern.</returns>
        public static BindingTemplateSource FromString(string pattern, bool ignoreCase = false)
        {
            IEnumerable <BindingTemplateToken> tokens = BindingTemplateParser.GetTokens(pattern);
            string capturePattern = BindingTemplateToken.BuildCapturePattern(tokens);

            RegexOptions options = RegexOptions.Compiled;

            if (ignoreCase)
            {
                options |= RegexOptions.IgnoreCase;
            }

            return(new BindingTemplateSource(pattern, new Regex(capturePattern, options)));
        }
Beispiel #2
0
        /// <summary>
        /// Creates a streaming iterator to scan the input string and generate valid template tokens.
        /// </summary>
        /// <param name="input">A template pattern string in supported format.</param>
        /// <returns>A sequence of tokens.</returns>
        /// <exception cref="FormatException">Thrown when the input has unbalanced brackets, parameter name doesn't
        /// match C# identifier definition, or some other content validation rule fails.</exception>
        public static IEnumerable <BindingTemplateToken> GetTokens(string input)
        {
            // Validate token rule is up-to-date and input string matches a pattern of sequence of tokens.
            // Ensure input string has no unrecognized chunks.
            Debug.Assert(Regex.IsMatch(input, ValidateGrammar));

            Regex grammarRegex = new Regex(TokenRule,
                                           RegexOptions.ExplicitCapture | RegexOptions.IgnorePatternWhitespace);

            const string EntirePatternGroupName = "0";

            string[] groupNames = grammarRegex.GetGroupNames().Where(
                s => !String.Equals(s, EntirePatternGroupName)).ToArray();

            // Outer loop iterates over all matched tokens in the input.
            foreach (Match m in grammarRegex.Matches(input))
            {
                // Inner loops scans over possible token type alternatives of currently matched token.
                // It could be escaped character, parameter, or literal chunk.
                foreach (var name in groupNames.Where(n => m.Groups[n].Success))
                {
                    Group namedGroup = m.Groups[name];
                    switch (name)
                    {
                    case "escape":
                        string value = Char.ToString(namedGroup.Value[0]);
                        yield return(BindingTemplateToken.NewLiteral(value));

                        break;

                    case "parameter":
                        if (String.IsNullOrEmpty(namedGroup.Value))
                        {
                            throw new FormatException(String.Format(
                                                          "Invalid template '{0}'. The parameter name at position {1} is empty.",
                                                          input, m.Index + 1));
                        }

                        BindingTemplateToken token;
                        try
                        {
                            token = BindingTemplateToken.NewExpression(namedGroup.Value);
                        }
                        catch (FormatException e)
                        {
                            throw new FormatException($"Invalid template '{input}'. {e.Message}");
                        }
                        yield return(token);

                        break;

                    case "literal":
                        yield return(BindingTemplateToken.NewLiteral(namedGroup.Value));

                        break;

                    case "unbalanced":
                        throw new FormatException(String.Format(
                                                      "Invalid template '{0}'. Missing {1} bracket at position {2}.",
                                                      input, namedGroup.Value[0] == '{' ? "closing" : "opening", m.Index + 1));

                    default:
                        Debug.Fail("Unsupported named group!");
                        break;
                    }
                }
            }
        }