@Nonnull
  private CSSSelectorAttribute _createSelectorAttribute(@Nonnull final CSSNode aNode) {
    _expectNodeType(aNode, ECSSNodeType.ATTRIB);
    final int nChildren = aNode.jjtGetNumChildren();

    // Check if a namespace prefix is present
    String sNamespacePrefix = null;
    int nOperatorIndex = 0;
    if (nChildren > 0 && ECSSNodeType.NAMESPACEPREFIX.isNode(aNode.jjtGetChild(0), m_eVersion)) {
      sNamespacePrefix = aNode.jjtGetChild(0).getText();
      nOperatorIndex = 1;
    }
    final String sAttrName = aNode.getText();

    CSSSelectorAttribute ret;
    if (nChildren == nOperatorIndex) {
      // Just check for existence of the attribute
      ret = new CSSSelectorAttribute(sNamespacePrefix, sAttrName);
    } else {
      final int nExpectedChildCount = nOperatorIndex + 2;
      if (nChildren != nExpectedChildCount)
        _throwUnexpectedChildrenCount(
            aNode,
            "Illegal number of children present ("
                + nChildren
                + ") - expected "
                + nExpectedChildCount);

      // With operator...
      final CSSNode aOperator = aNode.jjtGetChild(nOperatorIndex);
      _expectNodeType(aOperator, ECSSNodeType.ATTRIBOPERATOR);

      // ...and value
      final CSSNode aAttrValue = aNode.jjtGetChild(nOperatorIndex + 1);
      _expectNodeType(aAttrValue, ECSSNodeType.ATTRIBVALUE);

      ret =
          new CSSSelectorAttribute(
              sNamespacePrefix,
              sAttrName,
              ECSSAttributeOperator.getFromNameOrNull(aOperator.getText()),
              aAttrValue.getText());
    }
    ret.setSourceLocation(aNode.getSourceLocation());
    return ret;
  }
  @Nullable
  private ICSSSelectorMember _createSelectorMember(final CSSNode aNode) {
    final int nChildCount = aNode.jjtGetNumChildren();

    if (ECSSNodeType.NAMESPACEPREFIX.isNode(aNode, m_eVersion)
        || ECSSNodeType.ELEMENTNAME.isNode(aNode, m_eVersion)
        || ECSSNodeType.HASH.isNode(aNode, m_eVersion)
        || ECSSNodeType.CLASS.isNode(aNode, m_eVersion)) {
      if (nChildCount != 0)
        _throwUnexpectedChildrenCount(
            aNode, "CSS simple selector member expected 0 children and got " + nChildCount);
      final CSSSelectorSimpleMember ret = new CSSSelectorSimpleMember(aNode.getText());
      ret.setSourceLocation(aNode.getSourceLocation());
      return ret;
    }

    if (ECSSNodeType.ATTRIB.isNode(aNode, m_eVersion)) return _createSelectorAttribute(aNode);

    if (ECSSNodeType.SELECTORCOMBINATOR.isNode(aNode, m_eVersion)) {
      final String sText = aNode.getText();
      final ECSSSelectorCombinator eCombinator = ECSSSelectorCombinator.getFromNameOrNull(sText);
      if (eCombinator == null)
        s_aLogger.error("Failed to parse CSS selector combinator '" + sText + "'");
      return eCombinator;
    }

    if (ECSSNodeType.NEGATION.isNode(aNode, m_eVersion)) {
      // Note: no children don't make sense but are syntactically allowed!
      final List<CSSSelector> aNestedSelectors = new ArrayList<CSSSelector>();
      for (int i = 0; i < nChildCount; ++i) {
        final CSSNode aChildNode = aNode.jjtGetChild(0);
        final CSSSelector aSelector = _createSelector(aChildNode);
        aNestedSelectors.add(aSelector);
      }

      final CSSSelectorMemberNot ret = new CSSSelectorMemberNot(aNestedSelectors);
      ret.setSourceLocation(aNode.getSourceLocation());
      return ret;
    }

    if (ECSSNodeType.PSEUDO.isNode(aNode, m_eVersion)) {
      if (nChildCount == 0) {
        // E.g. ":focus" or ":hover"
        final CSSSelectorSimpleMember ret = new CSSSelectorSimpleMember(aNode.getText());
        ret.setSourceLocation(aNode.getSourceLocation());
        return ret;
      }

      if (nChildCount == 1) {
        final CSSNode aChildNode = aNode.jjtGetChild(0);
        if (ECSSNodeType.NTH.isNode(aChildNode, m_eVersion)) {
          // Handle nth. E.g. ":nth-child(even)" or ":nth-child(3n+1)"
          final CSSSelectorSimpleMember ret =
              new CSSSelectorSimpleMember(aNode.getText() + aChildNode.getText() + ")");
          ret.setSourceLocation(aNode.getSourceLocation());
          return ret;
        }

        // It's a function (e.g. ":lang(fr)")
        final CSSExpression aExpr = _createExpression(aChildNode);
        final CSSSelectorMemberFunctionLike ret =
            new CSSSelectorMemberFunctionLike(aNode.getText(), aExpr);
        ret.setSourceLocation(aNode.getSourceLocation());
        return ret;
      }

      throw new UnsupportedOperationException(
          "Not supporting pseudo-selectors with functions and "
              + nChildCount
              + " args: "
              + aNode.toString());
    }

    s_aLogger.error("Unsupported selector child: " + ECSSNodeType.getNodeName(aNode, m_eVersion));
    return null;
  }