/* * Parse a selector group (eg. E, F, G). In many/most cases there will be only one entry. */ private List<Selector> parseSelectorGroup(CSSTextScanner scan) throws SAXException { if (scan.empty()) return null; ArrayList<Selector> selectorGroup = new ArrayList<Selector>(1); Selector selector = new Selector(); while (!scan.empty()) { if (scan.nextSimpleSelector(selector)) { // If there is a comma, keep looping, otherwise break if (!scan.skipCommaWhitespace()) continue; // if not a comma, go back and check for next part of selector selectorGroup.add(selector); selector = new Selector(); } else break; } if (!selector.isEmpty()) selectorGroup.add(selector); return selectorGroup; }
/* * Scans for a CSS 'simple selector'. * Returns true if it found one. * Returns false if there was an error or the input is empty. */ public boolean nextSimpleSelector(Selector selector) throws SAXException { if (empty()) return false; int start = position; Combinator combinator = null; SimpleSelector selectorPart = null; if (!selector.isEmpty()) { if (consume('>')) { combinator = Combinator.CHILD; skipWhitespace(); } else if (consume('+')) { combinator = Combinator.FOLLOWS; skipWhitespace(); } } if (consume('*')) { selectorPart = new SimpleSelector(combinator, null); } else { String tag = nextIdentifier(); if (tag != null) { selectorPart = new SimpleSelector(combinator, tag); selector.addedElement(); } } while (!empty()) { if (consume('.')) { // ".foo" is equivalent to *[class="foo"] if (selectorPart == null) selectorPart = new SimpleSelector(combinator, null); String value = nextIdentifier(); if (value == null) throw new SAXException("Invalid \".class\" selector in <style> element"); selectorPart.addAttrib(CLASS, AttribOp.EQUALS, value); selector.addedAttributeOrPseudo(); continue; } if (consume('#')) { // "#foo" is equivalent to *[id="foo"] if (selectorPart == null) selectorPart = new SimpleSelector(combinator, null); String value = nextIdentifier(); if (value == null) throw new SAXException("Invalid \"#id\" selector in <style> element"); selectorPart.addAttrib(ID, AttribOp.EQUALS, value); selector.addedIdAttribute(); } if (selectorPart == null) break; // Now check for attribute selection and pseudo selectors if (consume('[')) { skipWhitespace(); String attrName = nextIdentifier(); String attrValue = null; if (attrName == null) throw new SAXException("Invalid attribute selector in <style> element"); skipWhitespace(); AttribOp op = null; if (consume('=')) op = AttribOp.EQUALS; else if (consume("~=")) op = AttribOp.INCLUDES; else if (consume("|=")) op = AttribOp.DASHMATCH; if (op != null) { skipWhitespace(); attrValue = nextAttribValue(); if (attrValue == null) throw new SAXException("Invalid attribute selector in <style> element"); skipWhitespace(); } if (!consume(']')) throw new SAXException("Invalid attribute selector in <style> element"); selectorPart.addAttrib(attrName, (op == null) ? AttribOp.EXISTS : op, attrValue); selector.addedAttributeOrPseudo(); continue; } if (consume(':')) { // skip pseudo int pseudoStart = position; if (nextIdentifier() != null) { if (consume('(')) { skipWhitespace(); if (nextIdentifier() != null) { skipWhitespace(); if (!consume(')')) { position = pseudoStart - 1; break; } } } selectorPart.addPseudo(input.substring(pseudoStart, position)); selector.addedAttributeOrPseudo(); } } break; } if (selectorPart != null) { selector.add(selectorPart); return true; } // Otherwise 'fail' position = start; return false; }