Esempio n. 1
0
        public static IList <ICssSelectorItem> CreateCssSelector(String selector)
        {
            IList <ICssSelectorItem> cssSelectorItems = new List <ICssSelectorItem>();
            Match itemMatcher   = selectorPattern.Match(selector);
            bool  isTagSelector = false;
            int   crc           = 0;

            while (itemMatcher.Success)
            {
                String selectorItem = itemMatcher.Groups[0].Value;
                crc += selectorItem.Length;
                switch (selectorItem[0])
                {
                case '#':
                    cssSelectorItems.Add(new CssIdSelector(selectorItem.Substring(1)));
                    break;

                case '.':
                    cssSelectorItems.Add(new CssClassSelector(selectorItem.Substring(1)));
                    break;

                case '[':
                    cssSelectorItems.Add(new CssAttributeSelector(selectorItem));
                    break;

                case ':':
                    cssSelectorItems.Add(new CssPseudoSelector(selectorItem));
                    break;

                case ' ':
                case '+':
                case '>':
                case '~':
                    if (cssSelectorItems.Count == 0)
                    {
                        return(null);
                    }
                    ICssSelectorItem lastItem = cssSelectorItems[cssSelectorItems.Count - 1];
                    ICssSelectorItem currItem = new CssSeparatorSelector(selectorItem[0]);
                    if (lastItem is CssSeparatorSelector)
                    {
                        if (selectorItem[0] == ' ')
                        {
                            break;
                        }
                        else if (lastItem.Separator == ' ')
                        {
                            cssSelectorItems[cssSelectorItems.Count - 1] = currItem;
                        }
                        else
                        {
                            return(null);
                        }
                    }
                    else
                    {
                        cssSelectorItems.Add(currItem);
                        isTagSelector = false;
                    }
                    break;

                default:     //and case '*':
                    if (isTagSelector)
                    {
                        return(null);
                    }
                    isTagSelector = true;
                    cssSelectorItems.Add(new CssTagSelector(selectorItem));
                    break;
                }
                itemMatcher = itemMatcher.NextMatch();
            }

            if (selector.Length == 0 || selector.Length != crc)
            {
                return(null);
            }
            return(cssSelectorItems);
        }
Esempio n. 2
0
        /// <summary>Parses the selector items.</summary>
        /// <param name="selector">
        /// the selectors in the form of a
        /// <see cref="System.String"/>
        /// </param>
        /// <returns>
        /// the resulting list of
        /// <see cref="iText.StyledXmlParser.Css.Selector.Item.ICssSelectorItem"/>
        /// </returns>
        public static IList <ICssSelectorItem> ParseSelectorItems(String selector)
        {
            IList <ICssSelectorItem> selectorItems = new List <ICssSelectorItem>();
            CssSelectorParserMatch   match         = new CssSelectorParserMatch(selector, selectorPattern);
            bool tagSelectorDescription            = false;

            while (match.Success())
            {
                String selectorItem = match.GetValue();
                char   firstChar    = selectorItem[0];
                switch (firstChar)
                {
                case '#': {
                    match.Next();
                    selectorItems.Add(new CssIdSelectorItem(selectorItem.Substring(1)));
                    break;
                }

                case '.': {
                    match.Next();
                    selectorItems.Add(new CssClassSelectorItem(selectorItem.Substring(1)));
                    break;
                }

                case '[': {
                    match.Next();
                    selectorItems.Add(new CssAttributeSelectorItem(selectorItem));
                    break;
                }

                case ':': {
                    AppendPseudoSelector(selectorItems, selectorItem, match);
                    break;
                }

                case ' ':
                case '+':
                case '>':
                case '~': {
                    match.Next();
                    if (selectorItems.Count == 0)
                    {
                        throw new ArgumentException(MessageFormatUtil.Format("Invalid token detected in the start of the selector string: {0}"
                                                                             , firstChar));
                    }
                    ICssSelectorItem         lastItem = selectorItems[selectorItems.Count - 1];
                    CssSeparatorSelectorItem curItem  = new CssSeparatorSelectorItem(firstChar);
                    if (lastItem is CssSeparatorSelectorItem)
                    {
                        if (curItem.GetSeparator() == ' ')
                        {
                            break;
                        }
                        else
                        {
                            if (((CssSeparatorSelectorItem)lastItem).GetSeparator() == ' ')
                            {
                                selectorItems[selectorItems.Count - 1] = curItem;
                            }
                            else
                            {
                                throw new ArgumentException(MessageFormatUtil.Format("Invalid selector description. Two consequent characters occurred: {0}, {1}"
                                                                                     , ((CssSeparatorSelectorItem)lastItem).GetSeparator(), curItem.GetSeparator()));
                            }
                        }
                    }
                    else
                    {
                        selectorItems.Add(curItem);
                        tagSelectorDescription = false;
                    }
                    break;
                }

                default: {
                    //and case '*':
                    match.Next();
                    if (tagSelectorDescription)
                    {
                        throw new InvalidOperationException("Invalid selector string");
                    }
                    tagSelectorDescription = true;
                    selectorItems.Add(new CssTagSelectorItem(selectorItem));
                    break;
                }
                }
            }
            if (selectorItems.Count == 0)
            {
                throw new ArgumentException("Selector declaration is invalid");
            }
            return(selectorItems);
        }
Esempio n. 3
0
        /// <summary>Checks if a node matches the selector.</summary>
        /// <param name="element">the node</param>
        /// <param name="lastSelectorItemInd">the index of the last selector</param>
        /// <returns>true, if there's a match</returns>
        private bool Matches(INode element, int lastSelectorItemInd)
        {
            if (!(element is IElementNode))
            {
                return(false);
            }
            if (lastSelectorItemInd < 0)
            {
                return(true);
            }
            //TODO: Consider pseudo-elements in SVG
            bool isPseudoElement = element is CssPseudoElementNode;

            for (int i = lastSelectorItemInd; i >= 0; i--)
            {
                if (isPseudoElement && selectorItems[lastSelectorItemInd] is CssPseudoElementSelectorItem && i < lastSelectorItemInd
                    )
                {
                    // Pseudo element selector item shall be at the end of the selector string
                    // and be single pseudo element selector item in it. All other selector items are checked against
                    // pseudo element node parent.
                    element         = element.ParentNode();
                    isPseudoElement = false;
                }
                ICssSelectorItem currentItem = selectorItems[i];
                if (currentItem is CssSeparatorSelectorItem)
                {
                    char separator = ((CssSeparatorSelectorItem)currentItem).GetSeparator();
                    switch (separator)
                    {
                    case '>': {
                        return(Matches(element.ParentNode(), i - 1));
                    }

                    case ' ': {
                        INode parent = element.ParentNode();
                        while (parent != null)
                        {
                            bool parentMatches = Matches(parent, i - 1);
                            if (parentMatches)
                            {
                                return(true);
                            }
                            else
                            {
                                parent = parent.ParentNode();
                            }
                        }
                        return(false);
                    }

                    case '~': {
                        INode parent = element.ParentNode();
                        if (parent != null)
                        {
                            int indexOfElement = parent.ChildNodes().IndexOf(element);
                            for (int j = indexOfElement - 1; j >= 0; j--)
                            {
                                if (Matches(parent.ChildNodes()[j], i - 1))
                                {
                                    return(true);
                                }
                            }
                        }
                        return(false);
                    }

                    case '+': {
                        INode parent = element.ParentNode();
                        if (parent != null)
                        {
                            int   indexOfElement  = parent.ChildNodes().IndexOf(element);
                            INode previousElement = null;
                            for (int j = indexOfElement - 1; j >= 0; j--)
                            {
                                if (parent.ChildNodes()[j] is IElementNode)
                                {
                                    previousElement = parent.ChildNodes()[j];
                                    break;
                                }
                            }
                            if (previousElement != null)
                            {
                                return(indexOfElement > 0 && Matches(previousElement, i - 1));
                            }
                        }
                        return(false);
                    }

                    default: {
                        return(false);
                    }
                    }
                }
                else
                {
                    if (!currentItem.Matches(element))
                    {
                        return(false);
                    }
                }
            }
            return(true);
        }
Esempio n. 4
0
        /// <summary>
        /// Resolves a pseudo selector, appends it to list and updates
        /// <see cref="CssSelectorParserMatch"/>
        /// in process.
        /// </summary>
        /// <param name="selectorItems">list of items to which new selector will be added to</param>
        /// <param name="pseudoSelector">the pseudo selector</param>
        /// <param name="match">
        /// the corresponding
        /// <see cref="CssSelectorParserMatch"/>
        /// that will be updated.
        /// </param>
        private static void AppendPseudoSelector(IList <ICssSelectorItem> selectorItems, String pseudoSelector, CssSelectorParserMatch
                                                 match)
        {
            pseudoSelector = pseudoSelector.ToLowerInvariant();
            int    start  = match.GetIndex() + pseudoSelector.Length;
            String source = match.GetSource();

            if (start < source.Length && source[start] == '(')
            {
                int bracketDepth = 1;
                int curr         = start + 1;
                while (bracketDepth > 0 && curr < source.Length)
                {
                    if (source[curr] == '(')
                    {
                        ++bracketDepth;
                    }
                    else
                    {
                        if (source[curr] == ')')
                        {
                            --bracketDepth;
                        }
                        else
                        {
                            if (source[curr] == '"' || source[curr] == '\'')
                            {
                                curr = CssUtils.FindNextUnescapedChar(source, source[curr], curr + 1);
                            }
                        }
                    }
                    ++curr;
                }
                if (bracketDepth == 0)
                {
                    match.Next(curr);
                    pseudoSelector += source.JSubstring(start, curr);
                }
                else
                {
                    match.Next();
                }
            }
            else
            {
                match.Next();
            }

            /*
             * This :: notation is introduced by the current document in order to establish a discrimination between
             * pseudo-classes and pseudo-elements.
             * For compatibility with existing style sheets, user agents must also accept the previous one-colon
             * notation for pseudo-elements introduced in CSS levels 1 and 2 (namely, :first-line, :first-letter, :before and :after).
             * This compatibility is not allowed for the new pseudo-elements introduced in this specification.
             */
            if (pseudoSelector.StartsWith("::"))
            {
                selectorItems.Add(new CssPseudoElementSelectorItem(pseudoSelector.Substring(2)));
            }
            else
            {
                if (pseudoSelector.StartsWith(":") && legacyPseudoElements.Contains(pseudoSelector.Substring(1)))
                {
                    selectorItems.Add(new CssPseudoElementSelectorItem(pseudoSelector.Substring(1)));
                }
                else
                {
                    ICssSelectorItem pseudoClassSelectorItem = CssPseudoClassSelectorItem.Create(pseudoSelector.Substring(1));
                    if (pseudoClassSelectorItem == null)
                    {
                        throw new ArgumentException(MessageFormatUtil.Format(iText.StyledXmlParser.LogMessageConstant.UNSUPPORTED_PSEUDO_CSS_SELECTOR
                                                                             , pseudoSelector));
                    }
                    selectorItems.Add(pseudoClassSelectorItem);
                }
            }
        }