private static bool TryParseSimpleSelector(CssStringStreamReader reader, out CssSimpleSelector selector)
        {
            selector = null;
            reader.SkipWhitespaceAndComments();

            switch (reader.CurrentChar)
            {
            case '#':
                reader.Advance();
                return
                    (reader.TryReadIdentifier(out var id) &&
                     TryParseSimpleSelector(reader, CssSimpleSelectorType.IdSelector, new CssQualifiedName(id), out selector));

            case '.':
                reader.Advance();
                return
                    (reader.TryReadIdentifier(out var className) &&
                     TryParseSimpleSelector(reader, CssSimpleSelectorType.ClassSelector, new CssQualifiedName(className), out selector));

            case '*':
                reader.Advance();
                return(TryParseUniversalSelector(reader, out selector));

            case '[':
            case ':':
                return(TryParseUniversalSelector(reader, out selector));

            default:
                return
                    (reader.TryReadQualifiedName(out var typeName) &&
                     TryParseSimpleSelector(reader, CssSimpleSelectorType.TypeSelector, typeName, out selector));
            }
        }
        private static bool TryParseCompoundSelector(CssStringStreamReader reader, out CssCompoundSelector selector)
        {
            var simpleSelectors = new List <CssSimpleSelector>();

            reader.SkipWhitespaceAndComments();
            while (!reader.IsEndOfStream)
            {
                if (!TryParseSimpleSelector(reader, out var simpleSelector))
                {
                    break;
                }
                simpleSelectors.Add(simpleSelector);
                if (IsCombinatorChar(reader.CurrentChar))
                {
                    break;
                }
            }
            if (simpleSelectors.Count == 0)
            {
                selector = null;
                return(false);
            }
            selector = new CssCompoundSelector(simpleSelectors.ToArray());
            return(true);
        }
        private static CssAttributeSelector[] ParseAttributeSelectors(CssStringStreamReader reader)
        {
            var selectors = new List <CssAttributeSelector>();

            while (reader.CurrentChar == '[')
            {
                reader.Advance();
                if (!reader.TryReadIdentifier(out var name) || !reader.TryReadCssAttributeSelectorOperation(out var op))
                {
                    return(null);
                }

                var value     = string.Empty;
                var wasString = false;
                if (op != CssAttributeSelectorOperation.Has)
                {
                    if (!reader.TryReadIdentifierOrString(out value, out wasString))
                    {
                        return(null);
                    }
                }

                reader.SkipWhitespaceAndComments();

                var explicitIgnoreCase = reader.CurrentChar == 'i';
                if (explicitIgnoreCase)
                {
                    reader.Advance();
                    reader.SkipWhitespaceAndComments();
                }

                if (reader.CurrentChar != ']')
                {
                    return(null);
                }

                reader.Advance();
                var valueType = wasString ? CssAttributeSelectorValueType.String : CssAttributeSelectorValueType.Identifier;
                selectors.Add(new CssAttributeSelector(name, value, op, valueType, explicitIgnoreCase));
            }
            return(selectors.ToArray());
        }
        /// <summary>
        /// Tries to parse a collection of properties (name value pairs, e.g. 'fill:#000') of a style body
        /// </summary>
        public static bool TryParseStyleRuleBodyProperties(CssStringStreamReader reader, out Dictionary <string, CssStylePropertyValue> properties)
        {
            properties = new Dictionary <string, CssStylePropertyValue>();

            reader.SkipWhitespaceAndComments();

            var builder = new StringBuilder();

            while (!reader.IsEndOfStream && reader.CurrentChar != '}')
            {
                if (!reader.TryReadIdentifier(out var name))
                {
                    return(false);
                }

                reader.SkipWhitespaceAndComments();
                if (!reader.Read(':'))
                {
                    return(false);
                }
                reader.SkipWhitespaceAndComments();

                while (reader.CurrentChar != ';' && reader.CurrentChar != '}' && !reader.IsEndOfStream)
                {
                    builder.Append(reader.CurrentChar);
                    reader.Advance();
                }

                properties[name] = new CssStylePropertyValue(builder.ToString());
                builder.Clear();

                if (reader.CurrentChar == ';')
                {
                    reader.Read(';');
                }

                reader.SkipWhitespaceAndComments();
            }

            return(true);
        }
        public static bool TryParseRules(CssStringStreamReader reader, out CssStyleRulesList rules)
        {
            rules = new CssStyleRulesList();
            reader.SkipWhitespaceAndComments();
            while (!reader.IsEndOfStream)
            {
                if (!TryParseSelectorList(reader, out var selectors) ||
                    !reader.SkipWhitespaceAndComments() ||
                    !reader.Read('{') ||
                    !TryParseStyleRuleBodyProperties(reader, out var properties) ||
                    !reader.SkipWhitespaceAndComments() ||
                    !reader.Read('}'))
                {
                    return(false);
                }

                rules.Add(new CssStyleRule(selectors, properties));
                reader.SkipWhitespaceAndComments();
            }
            return(true);
        }
 public static bool TryParseSelectorList(CssStringStreamReader reader, out CssSelectorList selectors)
 {
     selectors = new CssSelectorList();
     while (!reader.IsEndOfStream)
     {
         if (!TryParseComplexSelector(reader, out var complex))
         {
             break;
         }
         selectors.Add(Flatten(complex));
         reader.SkipWhitespaceAndComments();
         if (reader.CurrentChar != ',')
         {
             break;
         }
         reader.Advance();
     }
     return(selectors.Count > 0);
 }