@Nonnull
  @SuppressFBWarnings("IL_INFINITE_LOOP")
  private CSSPageRule _createPageRule(@Nonnull final CSSNode aNode) {
    _expectNodeType(aNode, ECSSNodeType.PAGERULE);

    final int nChildCount = aNode.jjtGetNumChildren();
    String sPseudoPage = null;
    int nStartIndex = 0;
    if (nChildCount > 0) {
      final CSSNode aFirstChild = aNode.jjtGetChild(0);
      if (ECSSNodeType.PSEUDOPAGE.isNode(aFirstChild, m_eVersion)) {
        sPseudoPage = aFirstChild.getText();
        nStartIndex++;
      }
    }

    final CSSPageRule ret = new CSSPageRule(sPseudoPage);
    ret.setSourceLocation(aNode.getSourceLocation());
    for (int nIndex = nStartIndex; nIndex < nChildCount; ++nIndex) {
      final CSSNode aChildNode = aNode.jjtGetChild(nIndex);

      if (ECSSNodeType.STYLEDECLARATIONLIST.isNode(aChildNode, m_eVersion)) {
        // Read all contained declarations
        final int nDecls = aChildNode.jjtGetNumChildren();
        for (int nDecl = 0; nDecl < nDecls; ++nDecl) {
          final CSSDeclaration aDeclaration = _createDeclaration(aChildNode.jjtGetChild(nDecl));
          if (aDeclaration != null) ret.addDeclaration(aDeclaration);
        }
      } else if (!ECSSNodeType.isErrorNode(aChildNode, m_eVersion))
        s_aLogger.error(
            "Unsupported page rule child: " + ECSSNodeType.getNodeName(aChildNode, m_eVersion));
    }
    return ret;
  }
  @Nonnull
  private CSSStyleRule _createStyleRule(@Nonnull final CSSNode aNode) {
    _expectNodeType(aNode, ECSSNodeType.STYLERULE);
    final CSSStyleRule ret = new CSSStyleRule();
    ret.setSourceLocation(aNode.getSourceLocation());
    boolean bSelectors = true;
    for (final CSSNode aChildNode : aNode) {
      if (ECSSNodeType.SELECTOR.isNode(aChildNode, m_eVersion)) {
        if (!bSelectors) s_aLogger.error("Found a selector after a declaration!");

        ret.addSelector(_createSelector(aChildNode));
      } else {
        // OK, we're after the selectors
        bSelectors = false;
        if (ECSSNodeType.STYLEDECLARATIONLIST.isNode(aChildNode, m_eVersion)) {
          // Read all contained declarations
          final int nDecls = aChildNode.jjtGetNumChildren();
          for (int nDecl = 0; nDecl < nDecls; ++nDecl) {
            final CSSNode aChildChildNode = aChildNode.jjtGetChild(nDecl);
            if (!ECSSNodeType.isErrorNode(aChildChildNode, m_eVersion)) {
              final CSSDeclaration aDeclaration = _createDeclaration(aChildChildNode);
              if (aDeclaration != null) ret.addDeclaration(aDeclaration);
            }
          }
        } else if (!ECSSNodeType.isErrorNode(aChildNode, m_eVersion))
          s_aLogger.error(
              "Unsupported child of "
                  + ECSSNodeType.getNodeName(aNode, m_eVersion)
                  + ": "
                  + ECSSNodeType.getNodeName(aChildNode, m_eVersion));
      }
    }
    return ret;
  }
  @Nonnull
  private CSSKeyframesRule _createKeyframesRule(@Nonnull final CSSNode aNode) {
    _expectNodeType(aNode, ECSSNodeType.KEYFRAMESRULE);
    final int nChildCount = aNode.jjtGetNumChildren();
    if (nChildCount == 0)
      _throwUnexpectedChildrenCount(
          aNode, "Expected at least 1 child but got " + nChildCount + "!");

    // Get the identifier (e.g. the default "@keyframes" or the non-standard
    // "@-webkit-keyframes")
    final String sKeyframesDeclaration = aNode.getText();

    // get the name of the animation
    final CSSNode aAnimationNameNode = aNode.jjtGetChild(0);
    _expectNodeType(aAnimationNameNode, ECSSNodeType.KEYFRAMESIDENTIFIER);
    final String sAnimationName = aAnimationNameNode.getText();

    final CSSKeyframesRule ret = new CSSKeyframesRule(sKeyframesDeclaration, sAnimationName);
    ret.setSourceLocation(aNode.getSourceLocation());

    // Get the key frame blocks
    int nIndex = 1;
    CSSKeyframesBlock aBlock = null;
    while (nIndex < nChildCount) {
      final CSSNode aChildNode = aNode.jjtGetChild(nIndex);
      if (ECSSNodeType.KEYFRAMESSELECTOR.isNode(aChildNode, m_eVersion)) {
        // Read all single selectors
        final List<String> aKeyframesSelectors = new ArrayList<String>();
        for (final CSSNode aSelectorChild : aChildNode) {
          _expectNodeType(aSelectorChild, ECSSNodeType.SINGLEKEYFRAMESELECTOR);
          aKeyframesSelectors.add(aSelectorChild.getText());
        }
        aBlock = new CSSKeyframesBlock(aKeyframesSelectors);
        aBlock.setSourceLocation(aChildNode.getSourceLocation());
        ret.addBlock(aBlock);
      } else if (ECSSNodeType.STYLEDECLARATIONLIST.isNode(aChildNode, m_eVersion)) {
        if (aBlock == null) throw new IllegalStateException("No keyframes block present!");

        // Read all contained declarations
        final int nDecls = aChildNode.jjtGetNumChildren();
        for (int nDecl = 0; nDecl < nDecls; ++nDecl) {
          final CSSDeclaration aDeclaration = _createDeclaration(aChildNode.jjtGetChild(nDecl));
          if (aDeclaration != null) aBlock.addDeclaration(aDeclaration);
        }
      } else if (!ECSSNodeType.isErrorNode(aChildNode, m_eVersion))
        s_aLogger.error(
            "Unsupported keyframes rule child: "
                + ECSSNodeType.getNodeName(aChildNode, m_eVersion));

      ++nIndex;
    }
    return ret;
  }
 @Nonnull
 private CSSFontFaceRule _createFontFaceRule(@Nonnull final CSSNode aNode) {
   _expectNodeType(aNode, ECSSNodeType.FONTFACERULE);
   final CSSFontFaceRule ret = new CSSFontFaceRule();
   ret.setSourceLocation(aNode.getSourceLocation());
   for (final CSSNode aChildNode : aNode) {
     if (ECSSNodeType.STYLEDECLARATIONLIST.isNode(aChildNode, m_eVersion)) {
       // Read all contained declarations
       final int nDecls = aChildNode.jjtGetNumChildren();
       for (int nDecl = 0; nDecl < nDecls; ++nDecl) {
         final CSSDeclaration aDeclaration = _createDeclaration(aChildNode.jjtGetChild(nDecl));
         if (aDeclaration != null) ret.addDeclaration(aDeclaration);
       }
     } else if (!ECSSNodeType.isErrorNode(aChildNode, m_eVersion))
       s_aLogger.error(
           "Unsupported font-face rule child: "
               + ECSSNodeType.getNodeName(aChildNode, m_eVersion));
   }
   return ret;
 }
  @Nonnull
  private CSSViewportRule _createViewportRule(@Nonnull final CSSNode aNode) {
    _expectNodeType(aNode, ECSSNodeType.VIEWPORTRULE);

    // Get the identifier (e.g. the default "@viewport" or the non-standard
    // "@-ms-viewport")
    final String sViewportDeclaration = aNode.getText();

    final CSSViewportRule ret = new CSSViewportRule(sViewportDeclaration);
    ret.setSourceLocation(aNode.getSourceLocation());
    for (final CSSNode aChildNode : aNode) {
      if (ECSSNodeType.STYLEDECLARATIONLIST.isNode(aChildNode, m_eVersion)) {
        // Read all contained declarations
        final int nDecls = aChildNode.jjtGetNumChildren();
        for (int nDecl = 0; nDecl < nDecls; ++nDecl) {
          final CSSDeclaration aDeclaration = _createDeclaration(aChildNode.jjtGetChild(nDecl));
          if (aDeclaration != null) ret.addDeclaration(aDeclaration);
        }
      } else if (!ECSSNodeType.isErrorNode(aChildNode, m_eVersion))
        s_aLogger.error(
            "Unsupported viewport rule child: " + ECSSNodeType.getNodeName(aChildNode, m_eVersion));
    }
    return ret;
  }