Пример #1
0
  /**
   * With the start token expected to be the first token of a selector group, create and issue the
   * group, then invoke handleDeclarationBlock. At exit the last token returned from the iterator is
   * expected to be '}'.
   */
  private void handleRuleSet(
      CssToken start, final CssTokenIterator iter, final CssContentHandler doc, CssErrorHandler err)
      throws CssException {

    char errChar = '{';
    try {
      List<CssSelector> selectors = handleSelectors(start, iter, err);
      errChar = '}';
      if (selectors == null) {
        // handleSelectors() has issued errors, we forward
        iter.next(MATCH_CLOSEBRACE);
        return;
      }
      if (debug) {
        checkState(iter.last.getChar() == '{');
        checkState(!selectors.isEmpty());
      }

      doc.selectors(selectors);

      handleDeclarationBlock(iter.next(), iter, doc, err);

      doc.endSelectors(selectors);

    } catch (NoSuchElementException nse) {
      err.error(
          new CssGrammarException(GRAMMAR_PREMATURE_EOF, iter.last.location, "'" + errChar + "'"));
      throw new PrematureEOFException();
    }

    if (debug) {
      checkState(iter.last.getChar() == '}');
    }
  }
Пример #2
0
  /**
   * With start inparam being the first significant token in a selector, build the selector group
   * (aka comma separated selectors), expected return when iter.last is '{'. On error, issue to
   * errorlistener, and return (caller will forward).
   *
   * @return A syntactically valid CssSelector list, or null if fail.
   * @throws CssException
   */
  private List<CssSelector> handleSelectors(
      CssToken start, CssTokenIterator iter, CssErrorHandler err) throws CssException {

    List<CssSelector> selectors = Lists.newArrayList();
    boolean end = false;
    while (true) { // comma loop
      CssSelector selector = new CssSelector(start.location);
      while (true) { // combinator loop
        CssSimpleSelectorSequence seq =
            CssSelectorConstructFactory.createSimpleSelectorSequence(start, iter, err);
        if (seq == null) {
          // errors already issued
          return null;
        }
        selector.components.add(seq);
        int idx = iter.index();
        start = iter.next();
        if (MATCH_OPENBRACE.apply(start)) {
          end = true;
          break;
        }
        if (MATCH_COMMA.apply(start)) {
          break;
        }

        CssSelectorCombinator comb = CssSelectorConstructFactory.createCombinator(start, iter, err);
        if (comb != null) {
          selector.components.add(comb);
          start = iter.next();
        } else if (iter.list.get(idx + 1).type == CssToken.Type.S) {
          selector.components.add(new CssSelectorCombinator(' ', start.location));
        } else {
          err.error(
              new CssGrammarException(
                  GRAMMAR_UNEXPECTED_TOKEN, iter.last.location, iter.last.chars));
          return null;
        }
      } // combinator loop
      selectors.add(selector);
      if (end) {
        break;
      }
      if (debug) {
        checkState(MATCH_COMMA.apply(start));
      }
      start = iter.next();
    } // comma loop
    return selectors;
  }
Пример #3
0
  /**
   * Append property value components to declaration.value, return false if fail with iter.last at
   * the the token which caused the fail.
   */
  private boolean handlePropertyValue(
      CssDeclaration declaration, CssToken start, CssTokenIterator iter, boolean isStyleAttribute) {
    // we dont worry about EOF here, throw to caller
    while (true) {

      if (start.type == CssToken.Type.IMPORTANT) {
        declaration.important = true;
      } else {
        CssConstruct cc =
            CssConstructFactory.create(
                start, iter, MATCH_SEMI_CLOSEBRACE, ContextRestrictions.PROPERTY_VALUE);
        if (cc == null) {
          return false;
        } else {
          declaration.components.add(cc);
        }
      }

      // if isStyleAttribute, then parse as declaration-list grammar,
      // i.e. no braces
      if ((isStyleAttribute && !iter.hasNext())
          || (MATCH_SEMI.apply(iter.peek()))
          || (!isStyleAttribute && MATCH_CLOSEBRACE.apply(iter.peek()))) {
        return declaration.components.size() > 0;
      } else {
        start = iter.next();
      }
    }
  }
Пример #4
0
  /** Parse a CSS style attribute. */
  public void parseStyleAttribute(
      final Reader reader, String systemID, final CssErrorHandler err, final CssContentHandler doc)
      throws IOException, CssException {
    CssTokenIterator iter = scan(reader, systemID, err);
    doc.startDocument();
    while (iter.hasNext()) {
      CssToken tk = iter.next();

      if (MATCH_SEMI.apply(tk)) {
        continue; // starting with ';' is allowed, Issue 238
      }
      try {

        CssDeclaration decl = handleDeclaration(tk, iter, doc, err, true);
        if (decl != null) {
          doc.declaration(decl);
        } else {
          // #handleDeclaration has issued errors
          return;
        }
      } catch (PrematureEOFException te) {
        // The subroutines report premature EOF to ErrHandler
        // on occurrence; if the listener rethrows it will
        // be a CssException so we don't catch it here.
        break;
      }
    }
    doc.endDocument();
  }
Пример #5
0
  /**
   * With start token being the first non-ignorable token inside the declaration block, iterate
   * issuing CssDeclaration objects until the block ends.
   */
  private void handleDeclarationBlock(
      CssToken start, CssTokenIterator iter, final CssContentHandler doc, CssErrorHandler err)
      throws CssException {

    while (true) {
      if (MATCH_CLOSEBRACE.apply(start)) {
        return;
      }
      CssDeclaration decl = handleDeclaration(start, iter, doc, err, false);
      try {
        if (decl != null) {
          doc.declaration(decl);
          if (debug) {
            checkState(MATCH_SEMI_CLOSEBRACE.apply(iter.last));
          }

          // continue or return: we may be at "; next decl" or "}" or ";}"
          if (MATCH_CLOSEBRACE.apply(iter.last)) {
            return;
          } else if (MATCH_SEMI.apply(iter.last) && MATCH_CLOSEBRACE.apply(iter.peek())) {
            iter.next();
            return;
          } else {
            if (debug) {
              checkState(MATCH_SEMI.apply(iter.last));
            }
            // we have ';', expect another decl
            start = iter.next(); // first token after ';'
          }

        } else {
          // #handleDeclaration returned null to signal error
          // #handleDeclaration has issued errors, we forward
          start = iter.next(MATCH_SEMI_CLOSEBRACE);
          if (MATCH_SEMI.apply(start)) {
            start = iter.next();
          }
        }
      } catch (NoSuchElementException nse) {
        err.error(
            new CssGrammarException(
                GRAMMAR_PREMATURE_EOF, iter.last.location, "';' " + Messages.get("or") + " '}'"));
        throw new PrematureEOFException();
      }
    }
  }
Пример #6
0
  /** Parse a CSS document. */
  public void parse(
      final Reader reader,
      final String systemID,
      final CssErrorHandler err,
      final CssContentHandler doc)
      throws IOException, CssException {

    CssTokenIterator iter = scan(reader, systemID, err);

    doc.startDocument();

    while (iter.hasNext(FILTER_S_CMNT_CDO_CDC)) {
      CssToken tk = iter.next(FILTER_S_CMNT_CDO_CDC);
      try {

        if (tk.type == CssToken.Type.ATKEYWORD) {
          handleAtRule(tk, iter, doc, err);
          if (debug) {
            checkArgument(MATCH_SEMI_CLOSEBRACE.apply(iter.last));
          }
        } else {
          handleRuleSet(tk, iter, doc, err);
          if (debug) {
            checkArgument(MATCH_CLOSEBRACE.apply(iter.last));
          }
        }

      } catch (PrematureEOFException te) {
        // The subroutines report premature EOF to ErrHandler
        // on occurrence; if the listener rethrows it will
        // be a CssException so we don't catch it here.
        break;
      }
    }

    doc.endDocument();
  }
Пример #7
0
  /**
   * With start token required to be an ATKEYWORD, collect at-rule parameters if any, and if the
   * at-rule has a block, invoke those handlers.
   */
  private void handleAtRule(
      CssToken start, CssTokenIterator iter, CssContentHandler doc, CssErrorHandler err)
      throws CssException {

    if (debug) {
      checkArgument(start.type == CssToken.Type.ATKEYWORD);
      checkArgument(iter.index() == iter.list.indexOf(start));
      checkArgument(iter.filter() == FILTER_S_CMNT);
    }

    CssAtRule atRule = new CssAtRule(start.getChars(), start.location);

    CssToken tk;
    try {
      while (true) {
        tk = iter.next();
        if (MATCH_SEMI_OPENBRACE.apply(tk)) {
          // ';' or '{', expected end
          atRule.hasBlock = tk.getChar() == '{';
          break;
        } else {
          CssConstruct param = handleAtRuleParam(tk, iter, doc, err);
          if (param == null) {
            // issue error, forward, then return
            err.error(
                new CssGrammarException(
                    GRAMMAR_UNEXPECTED_TOKEN, iter.last.location, iter.last.chars));
            // skip to atrule closebrace, ignoring any inner blocks
            int stack = 0;
            while (true) {
              CssToken tok = iter.next();
              if (MATCH_SEMI.apply(tok) && stack == 0) {
                return; // a non-block at rule
              } else if (MATCH_OPENBRACE.apply(tok)) {
                stack++;
              } else if (MATCH_CLOSEBRACE.apply(tok)) {
                if (stack == 1) {
                  break;
                }
                stack--;
              }
            }
            return;
          } else {
            atRule.components.add(param);
          }
        }
      }
    } catch (NoSuchElementException nse) {
      // UAs required to close any open constructs on premature EOF
      doc.startAtRule(atRule);
      err.error(
          new CssGrammarException(
              GRAMMAR_PREMATURE_EOF, iter.last.location, "';' " + Messages.get("or") + " '{'"));
      doc.endAtRule(atRule.getName().get());
      throw new PrematureEOFException();
    }

    if (debug) {
      checkArgument(MATCH_SEMI_OPENBRACE.apply(iter.last));
      checkArgument(iter.filter() == FILTER_S_CMNT);
    }

    // ending up here only on expected end
    doc.startAtRule(atRule);
    if (atRule.hasBlock) {
      try {
        if (hasRuleSet(atRule, iter)) {
          while (!MATCH_CLOSEBRACE.apply(iter.next())) {
            if (iter.last.type == CssToken.Type.ATKEYWORD) {
              handleAtRule(iter.last, iter, doc, err);
            } else {
              handleRuleSet(iter.last, iter, doc, err);
            }
          }
        } else {
          handleDeclarationBlock(iter.next(), iter, doc, err);
        }
      } catch (NoSuchElementException nse) {
        err.error(new CssGrammarException(GRAMMAR_PREMATURE_EOF, iter.last.location, "'}'"));
        doc.endAtRule(atRule.name.get());
        throw new PrematureEOFException();
      }
    }
    doc.endAtRule(atRule.name.get());
  }
Пример #8
0
  /**
   * With start expected to be an IDENT token representing the property name, build the declaration
   * and return after hitting ';' or '}'. On error, issue to errhandler, return null, caller
   * forwards.
   */
  private CssDeclaration handleDeclaration(
      CssToken name,
      CssTokenIterator iter,
      CssContentHandler doc,
      CssErrorHandler err,
      boolean isStyleAttribute)
      throws CssException {

    if (name.type != CssToken.Type.IDENT) {
      err.error(
          new CssGrammarException(
              GRAMMAR_EXPECTING_TOKEN,
              name.location,
              name.getChars(),
              Messages.get("a_property_name")));
      return null;
    }

    CssDeclaration declaration = new CssDeclaration(name.getChars(), name.location);

    try {
      if (!MATCH_COLON.apply(iter.next())) {
        err.error(
            new CssGrammarException(
                GRAMMAR_EXPECTING_TOKEN, name.location, iter.last.getChars(), ":"));
        return null;
      }
    } catch (NoSuchElementException nse) {
      err.error(new CssGrammarException(GRAMMAR_PREMATURE_EOF, iter.last.location, ":"));
      throw new PrematureEOFException();
    }

    try {
      while (true) {
        CssToken value = iter.next();
        if (MATCH_SEMI_CLOSEBRACE.apply(value)) {
          if (declaration.components.size() < 1) {
            err.error(
                new CssGrammarException(
                    GRAMMAR_EXPECTING_TOKEN,
                    iter.last.location,
                    value.getChar(),
                    Messages.get("a_property_value")));
            return null;
          } else {
            return declaration;
          }
        } else {
          if (!handlePropertyValue(declaration, value, iter, isStyleAttribute)) {
            err.error(
                new CssGrammarException(
                    GRAMMAR_UNEXPECTED_TOKEN, iter.last.location, iter.last.getChars()));
            return null;
          } else {
            if (isStyleAttribute && !iter.hasNext()) {
              return declaration;
            }
          }
        }
      }
    } catch (NoSuchElementException nse) {
      err.error(
          new CssGrammarException(
              GRAMMAR_PREMATURE_EOF, iter.last.location, "';' " + Messages.get("or") + " '}'"));
      throw new PrematureEOFException();
    }
  }