/// <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); }
/// <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); } } }