protected SimpleSelectorSequence simpleSelectorSequence() throws ParserException {

        List<SimpleSelector> simpleSelectorList = new ArrayList<SimpleSelector>();
        StringBuilder sb = new StringBuilder();
        boolean hasSimpleSelector = false;
        int initialPos = pos;
        if (current == '*') {
            //universal selector
            sb.append(current);
            hasSimpleSelector = true;
            TypeSelector ts = new TypeSelector("*", new Context(content, "*", initialPos, pos + 1));
            parserListener.typeSelector(ts);
            simpleSelectorList.add(ts);
            next();
        } else if (Character.isLetter(current)) {
            //type selector
            String type = identifier();
            sb.append(type);
            hasSimpleSelector = true;
            TypeSelector ts = new TypeSelector(type, new Context(content, type, initialPos, pos));
            parserListener.typeSelector(ts);
            simpleSelectorList.add(ts);
        }

        int selectorCount = 0;
        boolean hasPseudoElement = false;
        while (!end()) {
        	int initialLoopPos = pos;
            if (current == '.') {
                //class selector
                sb.append(current);
                next();
                String className = !end() ? identifier() : null;
                if (className == null || className.isEmpty()) {
                    throw new ParserException("Expected className at position " + pos);
                }
                sb.append(className);
                ClassSelector cs = new ClassSelector(className, new Context(content, "." + className, initialLoopPos, pos));
                simpleSelectorList.add(cs);
                parserListener.classSelector(cs);
            } else if (current == '#') {
                //hash selector
                sb.append(current);
                next();
                String id = !end() ? identifier() : null;
                if (id == null || id.isEmpty()) {
                    throw new ParserException("Expected id at position " + pos);
                }
                HashSelector hs = new HashSelector(id, new Context(content, "#" + id, initialLoopPos, pos));
                simpleSelectorList.add(hs);
                parserListener.idSelector(hs);
                sb.append(id);
            } else if (current == '[') {
                //attribute selectors
                sb.append(current);
                next();
                AttributeSelector as = attribute();
                simpleSelectorList.add(as);
                parserListener.attributeSelector(as);
                sb.append(as);
                sb.append(']');
            } else if (current == ':') {
                //pseudo-element, pseudo-class or negation
                sb.append(current);
                next();
                if (end()) {
                    throw new ParserException("Expected pseudo-element or pseudo-class at " + pos);
                }
                boolean doubleColon = false;
                if (current == ':') {
                    doubleColon = true;
                    sb.append(current);
                    next();
                }
                String ident = !end() ? identifier() : null;
                if (ident == null || ident.isEmpty()) {
                    throw new ParserException("Expected identifier at position " + pos);
                }
                boolean special = isPseudoSpecialCase(ident);
                if (special || doubleColon) {
                    //pseudo-element (double colon or special cases)
                    //allow just one
                    if (hasPseudoElement) {
                        throw new ParserException("Only one pseudo-element is allowed for each simple selector and a second one was found at position " + pos);
                    }
                    PseudoSelector ps = pseudo(ident, PseudoType.PSEUDO_ELEMENT, doubleColon);
                    simpleSelectorList.add(ps);
                    parserListener.pseudoSelector(ps);
                    sb.append(ps);
                    hasPseudoElement = true;
                } else if ("not".equalsIgnoreCase(ident)) {
                    //negation
                    NegationSelector n = negation(selectorCount);
                    simpleSelectorList.add(n);
                    sb.append(ident);
                    sb.append('(');
                    sb.append(n);
                    sb.append(')');
                } else {
                    //pseudo-class
                    PseudoSelector ps = pseudo(ident, PseudoType.PSEUDO_CLASS, false);
                    simpleSelectorList.add(ps);
                    parserListener.pseudoSelector(ps);
                    sb.append(ps);
                }
            } else {
                break;
            }
            selectorCount++;
        }
        if (!hasSimpleSelector && selectorCount == 0) {
            throw new ParserException("No real selector found at position " + pos);
        }

        return new SimpleSelectorSequence(simpleSelectorList, new Context(content, sb.toString(), initialPos, pos));

    }
protected SimpleSelectorSequence simpleSelectorSequence() throws ParserException {

        List<SimpleSelector> simpleSelectorList = new ArrayList<SimpleSelector>();
        StringBuilder sb = new StringBuilder();
        boolean hasSimpleSelector = false;
        int initialPos = pos;
        if (current == '*') {
            
            sb.append(current);
            hasSimpleSelector = true;
            TypeSelector ts = new TypeSelector("*", new Context(content, "*", initialPos, pos + 1));
            parserListener.typeSelector(ts);
            simpleSelectorList.add(ts);
            next();
        } else if (Character.isLetter(current)) {
            
            String type = identifier();
            sb.append(type);
            hasSimpleSelector = true;
            TypeSelector ts = new TypeSelector(type, new Context(content, type, initialPos, pos));
            parserListener.typeSelector(ts);
            simpleSelectorList.add(ts);
        }

        int selectorCount = 0;
        boolean hasPseudoElement = false;
        while (!end()) {
        	int initialLoopPos = pos;
            if (current == '.') {
                
                sb.append(current);
                next();
                String className = !end() ? identifier() : null;
                if (className == null || className.isEmpty()) {
                    throw new ParserException("Expected className at position " + pos);
                }
                sb.append(className);
                ClassSelector cs = new ClassSelector(className, new Context(content, "." + className, initialLoopPos, pos));
                simpleSelectorList.add(cs);
                parserListener.classSelector(cs);
            } else if (current == '#') {
                
                sb.append(current);
                next();
                String id = !end() ? identifier() : null;
                if (id == null || id.isEmpty()) {
                    throw new ParserException("Expected id at position " + pos);
                }
                HashSelector hs = new HashSelector(id, new Context(content, "#" + id, initialLoopPos, pos));
                simpleSelectorList.add(hs);
                parserListener.idSelector(hs);
                sb.append(id);
            } else if (current == '[') {
                
                sb.append(current);
                next();
                AttributeSelector as = attribute();
                simpleSelectorList.add(as);
                parserListener.attributeSelector(as);
                sb.append(as);
                sb.append(']');
            } else if (current == ':') {
                
                sb.append(current);
                next();
                if (end()) {
                    throw new ParserException("Expected pseudo-element or pseudo-class at " + pos);
                }
                boolean doubleColon = false;
                if (current == ':') {
                    doubleColon = true;
                    sb.append(current);
                    next();
                }
                String ident = !end() ? identifier() : null;
                if (ident == null || ident.isEmpty()) {
                    throw new ParserException("Expected identifier at position " + pos);
                }
                boolean special = isPseudoSpecialCase(ident);
                if (special || doubleColon) {
                    
                    
                    if (hasPseudoElement) {
                        throw new ParserException("Only one pseudo-element is allowed for each simple selector and a second one was found at position " + pos);
                    }
                    PseudoSelector ps = pseudo(ident, PseudoType.PSEUDO_ELEMENT, doubleColon);
                    simpleSelectorList.add(ps);
                    parserListener.pseudoSelector(ps);
                    sb.append(ps);
                    hasPseudoElement = true;
                } else if ("not".equalsIgnoreCase(ident)) {
                    
                    NegationSelector n = negation(selectorCount);
                    simpleSelectorList.add(n);
                    sb.append(ident);
                    sb.append('(');
                    sb.append(n);
                    sb.append(')');
                } else {
                    
                    PseudoSelector ps = pseudo(ident, PseudoType.PSEUDO_CLASS, false);
                    simpleSelectorList.add(ps);
                    parserListener.pseudoSelector(ps);
                    sb.append(ps);
                }
            } else {
                break;
            }
            selectorCount++;
        }
        if (!hasSimpleSelector && selectorCount == 0) {
            throw new ParserException("No real selector found at position " + pos);
        }

        return new SimpleSelectorSequence(simpleSelectorList, new Context(content, sb.toString(), initialPos, pos));

    }
protected SimpleSelectorSequence simpleSelectorSequence ( ) throws ParserException { List < SimpleSelector > simpleSelectorList = new ArrayList < SimpleSelector > ( ) ; StringBuilder sb = new StringBuilder ( ) ; boolean hasSimpleSelector = false ; int initialPos = pos ; if ( current == ' ' ) { //universal selector
 sb . append ( current ) ; hasSimpleSelector = true ; TypeSelector ts = new TypeSelector ( "*" , new Context ( content , "*" , initialPos , pos + 1 ) ) ; parserListener . typeSelector ( ts ) ; simpleSelectorList . add ( ts ) ; next ( ) ; } else if ( Character . isLetter ( current ) ) { //type selector
 String type = identifier ( ) ; sb . append ( type ) ; hasSimpleSelector = true ; TypeSelector ts = new TypeSelector ( type , new Context ( content , type , initialPos , pos ) ) ; parserListener . typeSelector ( ts ) ; simpleSelectorList . add ( ts ) ; } int selectorCount = 0 ; boolean hasPseudoElement = false ; while ( ! end ( ) ) { int initialLoopPos = pos ; if ( current == ' ' ) { //class selector
 sb . append ( current ) ; next ( ) ; String className = ! end ( ) ? identifier ( ) : null ; if ( className == null || className . isEmpty ( ) ) { throw new ParserException ( "Expected className at position " + pos ) ; } sb . append ( className ) ; ClassSelector cs = new ClassSelector ( className , new Context ( content , "." + className , initialLoopPos , pos ) ) ; simpleSelectorList . add ( cs ) ; parserListener . classSelector ( cs ) ; } else if ( current == ' ' ) { //hash selector
 sb . append ( current ) ; next ( ) ; String id = ! end ( ) ? identifier ( ) : null ; if ( id == null || id . isEmpty ( ) ) { throw new ParserException ( "Expected id at position " + pos ) ; } HashSelector hs = new HashSelector ( id , new Context ( content , "#" + id , initialLoopPos , pos ) ) ; simpleSelectorList . add ( hs ) ; parserListener . idSelector ( hs ) ; sb . append ( id ) ; } else if ( current == ' ' ) { //attribute selectors
 sb . append ( current ) ; next ( ) ; AttributeSelector as = attribute ( ) ; simpleSelectorList . add ( as ) ; parserListener . attributeSelector ( as ) ; sb . append ( as ) ; sb . append ( ' ' ) ; } else if ( current == ' ' ) { //pseudo-element, pseudo-class or negation
 sb . append ( current ) ; next ( ) ; if ( end ( ) ) { throw new ParserException ( "Expected pseudo-element or pseudo-class at " + pos ) ; } boolean doubleColon = false ; if ( current == ' ' ) { doubleColon = true ; sb . append ( current ) ; next ( ) ; } String ident = ! end ( ) ? identifier ( ) : null ; if ( ident == null || ident . isEmpty ( ) ) { throw new ParserException ( "Expected identifier at position " + pos ) ; } boolean special = isPseudoSpecialCase ( ident ) ; if ( special || doubleColon ) { //pseudo-element (double colon or special cases)
 //allow just one
 if ( hasPseudoElement ) { throw new ParserException ( "Only one pseudo-element is allowed for each simple selector and a second one was found at position " + pos ) ; } PseudoSelector ps = pseudo ( ident , PseudoType . PSEUDO_ELEMENT , doubleColon ) ; simpleSelectorList . add ( ps ) ; parserListener . pseudoSelector ( ps ) ; sb . append ( ps ) ; hasPseudoElement = true ; } else if ( "not" . equalsIgnoreCase ( ident ) ) { //negation
 NegationSelector n = negation ( selectorCount ) ; simpleSelectorList . add ( n ) ; sb . append ( ident ) ; sb . append ( ' ' ) ; sb . append ( n ) ; sb . append ( ' ' ) ; } else { //pseudo-class
 PseudoSelector ps = pseudo ( ident , PseudoType . PSEUDO_CLASS , false ) ; simpleSelectorList . add ( ps ) ; parserListener . pseudoSelector ( ps ) ; sb . append ( ps ) ; } } else { break ; } selectorCount ++ ; } if ( ! hasSimpleSelector && selectorCount == 0 ) { throw new ParserException ( "No real selector found at position " + pos ) ; } return new SimpleSelectorSequence ( simpleSelectorList , new Context ( content , sb . toString ( ) , initialPos , pos ) ) ; }
simple_selector_sequence
: [ type_selector | universal ]
[ HASH | class | attrib | pseudo | negation ]*
| [ HASH | class | attrib | pseudo | negation ]+

type_selector
: [ namespace_prefix ]? element_name

namespace_prefix ** not implemented
: [ IDENT | '*' ]? '|'

element_name
: IDENT

universal
: [ namespace_prefix ]? '*'

class
: '.' IDENT
simple_selector_sequence : [ type_selector | universal ] [ HASH | class | attrib | pseudo | negation ] * | [ HASH | class | attrib | pseudo | negation ] +