Ejemplo n.º 1
0
        public static BoundExpression Parse(string expression,
                                            Func <string, bool, string, IValueProvider> contextualResource)
        {
            if (expression == null)
            {
                throw new ArgumentNullException(nameof(expression));
            }

            var i = 0;

            if (expression.StartsWith("\\"))
            {
                i = 1;
            }
            else if (expression.StartsWith("@"))
            {
                return(new BoundExpression(expression.Substring(1)));
            }

            var  resources         = new List <IValueProvider>();
            var  stringFormat      = new StringBuilder();
            var  resourceType      = new StringBuilder();
            var  resourceName      = new StringBuilder();
            var  resourceConverter = new StringBuilder();
            var  resourceFormat    = new StringBuilder();
            var  oneTimeBind       = false;
            var  length            = expression.Length;
            char c;

outside:
            if (i == length)
            {
                var format = stringFormat.ToString();
                return(new BoundExpression(resources, format == "{0}" ? null : format));
            }

            c = expression[i];
            if (c == '{')
            {
                stringFormat.Append('{');
                if (++i == expression.Length)
                {
                    throw new FormatException("Invalid unescaped '{' at end of input.");
                }

                if (expression[i] == '{')
                {
                    i++;
                    stringFormat.Append('{');
                    goto outside;
                }

                goto readResource;
            }

            if (c == '}')
            {
                stringFormat.Append('}');
                if (++i == expression.Length)
                {
                    throw new FormatException("Invalid unescaped '}' at end of input.");
                }

                if (expression[i] == '}')
                {
                    i++;
                    stringFormat.Append('}');
                    goto outside;
                }

                throw new FormatException("Invalid unescaped '}'.");
            }

            stringFormat.Append(c);
            i++;
            goto outside;

readResource:
            // A leading ^ indicates one time binding for contextual resources.
            if (expression[i] == '^')
            {
                if (++i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }

                oneTimeBind = true;
            }

            // Resource type.
            while (char.IsLetter(c = expression[i]))
            {
                resourceType.Append(c);
                if (++i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }
            }

            // Skip whitespace.
            while (char.IsWhiteSpace(expression[i]))
            {
                if (++i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }
            }

            // Resource name.
            if (expression[i] == '\'')
            {
                i++;
                if (i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }

                while ((c = expression[i]) != '\'')
                {
                    resourceName.Append(c);
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }
                }

                i++;
                if (i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }
            }
            else
            {
                while ((c = expression[i]) != ',' && c != ':' && c != '|')
                {
                    if (c == '{')
                    {
                        if (++i == length)
                        {
                            throw new FormatException("Unexpected end of input.");
                        }

                        if (expression[i] != '{')
                        {
                            throw new FormatException("Invalid unescaped '{'.");
                        }
                    }
                    else if (c == '}')
                    {
                        if (++i == length)
                        {
                            goto addResource;
                        }

                        if (expression[i] != '}')
                        {
                            goto addResource;
                        }
                    }

                    resourceName.Append(c);
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }
                }
            }

            // Skip whitespace between name and converter/format/end.
            while (char.IsWhiteSpace(expression[i]))
            {
                if (++i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }
            }

            c = expression[i];
            if (c == '}')
            {
                // Resource can close at this point assuming no converter and no string format.
                if (++i == length)
                {
                    goto addResource;
                }

                if (expression[i] != '}')
                {
                    goto addResource;
                }

                throw new FormatException("Invalid '}}' sequence.");
            }

            // Value converter, read while character is letter.
            if (c == '|')
            {
                if (++i == length)
                {
                    throw new FormatException("Unexpected end of input.");
                }

                while (char.IsLetter(c = expression[i]))
                {
                    resourceConverter.Append(c);
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }
                }

                // Skip whitespace between converter to parameter/format/end.
                while (char.IsWhiteSpace(expression[i]))
                {
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }
                }

                // Check for value converter parameter.
                if (c == '(')
                {
                    do
                    {
                        if (++i == length)
                        {
                            throw new FormatException("Unexpected end of input.");
                        }
                    } while (char.IsWhiteSpace(expression[i]));

                    c = expression[i];
                    if (c == '\'')
                    {
                        resourceConverter.Append(':');
                        var converterParameter = new StringBuilder();
                        while (true)
                        {
                            if (++i == length)
                            {
                                throw new FormatException("Unexpected end of input.");
                            }

                            c = expression[i];
                            if (c != '\'')
                            {
                                converterParameter.Append(c);
                            }
                            else
                            {
                                if (++i == length)
                                {
                                    throw new FormatException("Unexpected end of input.");
                                }

                                c = expression[i];
                                if (c == '\'')
                                {
                                    converterParameter.Append('\'');
                                }
                                else
                                {
                                    // End of string
                                    while (char.IsWhiteSpace(expression[i]))
                                    {
                                        if (++i == length)
                                        {
                                            throw new FormatException("Unexpected end of input.");
                                        }
                                    }

                                    c = expression[i];
                                    if (c != ')')
                                    {
                                        throw new FormatException(
                                                  "Expected closing parenthesis after converter parameter.");
                                    }

                                    if (++i == length)
                                    {
                                        throw new FormatException("Unexpected end of input.");
                                    }

                                    c = expression[i];
                                    break;
                                }
                            }
                        }

                        resourceConverter
                        .Append('\'')
                        .Append(converterParameter);
                    }
                    else if (c != ')')
                    {
                        resourceConverter.Append(':');
                        var converterParameter = new StringBuilder();
                        converterParameter.Append(c);
                        while (true)
                        {
                            if (++i == length)
                            {
                                throw new FormatException("Unexpected end of input.");
                            }

                            c = expression[i];
                            if (char.IsWhiteSpace(c))
                            {
                                do
                                {
                                    if (++i == length)
                                    {
                                        throw new FormatException("Unexpected end of input.");
                                    }
                                } while (char.IsWhiteSpace(expression[i]));

                                c = expression[i];
                                if (c != ')')
                                {
                                    throw new FormatException("Expected closing parenthesis after converter parameter.");
                                }
                            }

                            if (c == ')')
                            {
                                if (++i == length)
                                {
                                    throw new FormatException("Unexpected end of input.");
                                }

                                c = expression[i];
                                break;
                            }

                            converterParameter.Append(c);
                        }

                        resourceConverter.Append(converterParameter.ToString().ToLowerInvariant());
                    }
                }

                // Skip whitespace between converter/parameter to format/end.
                while (char.IsWhiteSpace(expression[i]))
                {
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }
                }

                if (c == '}')
                {
                    if (++i == length)
                    {
                        goto addResource;
                    }

                    if (expression[i] != '}')
                    {
                        goto addResource;
                    }

                    throw new FormatException("Converter name cannot contain braces.");
                }
            }

            // String format, read until single '}'.
            if (c == ',' || c == ':')
            {
                resourceFormat.Append(c);
                while (true)
                {
                    if (++i == length)
                    {
                        throw new FormatException("Unexpected end of input.");
                    }

                    c = expression[i];
                    if (c == '}')
                    {
                        if (++i == length)
                        {
                            goto addResource;
                        }

                        if (expression[i] != '}')
                        {
                            goto addResource;
                        }

                        resourceFormat.Append('}');
                    }

                    resourceFormat.Append(c);
                }
            }

            throw new FormatException($"Invalid character '{c}'");

addResource:
            var key = resourceName.ToString();
            IValueProvider resource;
            var            converter          = resourceConverter.ToString();
            var            resourceTypeString = resourceType.ToString();

            switch (resourceTypeString)
            {
            case "Binding":
                resource = new PropertyBinding(key, false, converter);
                break;

            case "Property":
                resource = new PropertyBinding(key, true, converter);
                break;

            case "StaticResource":
                resource = new StaticResource(key, converter);
                break;

            case "DynamicResource":
                resource = new DynamicResource(key, converter);
                break;

            case "ContextBinding":
                resource = new ContextPropertyBinding(key, false, converter);
                break;

            case "ContextProperty":
                resource = new ContextPropertyBinding(key, true, converter);
                break;

            case "Env":
                resource = new EnvironmentBinding(key, oneTimeBind, converter);
                break;

            case "FileBinding":
                resource = new FileBinding(key, true, converter);
                break;

            case "File":
                resource = new FileBinding(key, false, converter);
                break;

            default:
                resource = contextualResource?.Invoke(resourceTypeString + key, oneTimeBind, converter);
                if (resource != null)
                {
                    break;
                }

                throw new FormatException("Invalid resource type.");
            }

            var index = resources.IndexOf(resource);

            if (index == -1)
            {
                index = resources.Count;
                resources.Add(resource);
            }

            stringFormat.Append(index);
            if (resourceFormat.Length != 0)
            {
                stringFormat.Append(resourceFormat);
            }

            stringFormat.Append('}');

            resourceType.Clear();
            resourceName.Clear();
            resourceFormat.Clear();
            resourceConverter.Clear();
            oneTimeBind = false;
            goto outside;
        }