summaryrefslogtreecommitdiffstats
path: root/theme-compiler/src/com/vaadin/sass/parser/Parser.jj
diff options
context:
space:
mode:
Diffstat (limited to 'theme-compiler/src/com/vaadin/sass/parser/Parser.jj')
-rw-r--r--theme-compiler/src/com/vaadin/sass/parser/Parser.jj2463
1 files changed, 2463 insertions, 0 deletions
diff --git a/theme-compiler/src/com/vaadin/sass/parser/Parser.jj b/theme-compiler/src/com/vaadin/sass/parser/Parser.jj
new file mode 100644
index 0000000000..717094a763
--- /dev/null
+++ b/theme-compiler/src/com/vaadin/sass/parser/Parser.jj
@@ -0,0 +1,2463 @@
+/* -*-java-extended-*-
+ * Copyright (c) 1999 World Wide Web Consortium
+ * (Massachusetts Institute of Technology, Institut National de Recherche
+ * en Informatique et en Automatique, Keio University).
+ * All Rights Reserved. http://www.w3.org/Consortium/Legal/
+ *
+ * $Id: Parser.jj,v 1.15 2000/10/27 21:09:37 plehegar Exp $
+ */
+
+options {
+ IGNORE_CASE = true;
+ STATIC = false;
+ USER_CHAR_STREAM = true;
+ /* DEBUG_TOKEN_MANAGER = true;
+ DEBUG_PARSER = true; */
+}
+
+PARSER_BEGIN(Parser)
+
+package com.vaadin.sass.parser;
+
+import java.io.*;
+import java.net.*;
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.Map;
+
+import org.w3c.css.sac.ConditionFactory;
+import org.w3c.css.sac.Condition;
+import org.w3c.css.sac.SelectorFactory;
+import org.w3c.css.sac.SelectorList;
+import org.w3c.css.sac.Selector;
+import org.w3c.css.sac.SimpleSelector;
+import org.w3c.css.sac.DocumentHandler;
+import org.w3c.css.sac.InputSource;
+import org.w3c.css.sac.ErrorHandler;
+import org.w3c.css.sac.CSSException;
+import org.w3c.css.sac.CSSParseException;
+import org.w3c.css.sac.Locator;
+import org.w3c.css.sac.LexicalUnit;
+
+import org.w3c.flute.parser.selectors.SelectorFactoryImpl;
+import org.w3c.flute.parser.selectors.ConditionFactoryImpl;
+
+import org.w3c.flute.util.Encoding;
+
+import com.vaadin.sass.handler.*;
+
+import com.vaadin.sass.tree.*;
+
+/**
+ * A CSS2 parser
+ *
+ * @author Philippe Le H�garet
+ * @version $Revision: 1.15 $
+ */
+public class Parser implements org.w3c.css.sac.Parser {
+
+ // replaces all \t, \n, etc with this StringBuffer.
+ static final StringBuilder SPACE = new StringBuilder(" ");
+
+ // the document handler for the parser
+ protected SCSSDocumentHandlerImpl documentHandler;
+ // the error handler for the parser
+ protected ErrorHandler errorHandler;
+ // the input source for the parser
+ protected InputSource source;
+
+ protected ConditionFactory conditionFactory;
+ protected SelectorFactory selectorFactory;
+
+ // temporary place holder for pseudo-element ...
+ private String pseudoElt;
+
+ /**
+ * Creates a new Parser
+ */
+ public Parser() {
+ this((CharStream) null);
+ }
+
+ /**
+ * @@TODO
+ * @exception CSSException Not yet implemented
+ */
+ public void setLocale(Locale locale) throws CSSException {
+ throw new CSSException(CSSException.SAC_NOT_SUPPORTED_ERR);
+ }
+
+ /**
+ * Set the document handler for this parser
+ */
+ public void setDocumentHandler(DocumentHandler handler) {
+ this.documentHandler = (SCSSDocumentHandlerImpl) handler;
+ }
+
+ public void setSelectorFactory(SelectorFactory selectorFactory) {
+ this.selectorFactory = selectorFactory;
+ }
+
+ public void setConditionFactory(ConditionFactory conditionFactory) {
+ this.conditionFactory = conditionFactory;
+ }
+
+ /**
+ * Set the error handler for this parser
+ */
+ public void setErrorHandler(ErrorHandler error) {
+ this.errorHandler = error;
+ }
+
+ /**
+ * Main parse methods
+ *
+ * @param source the source of the style sheet.
+ * @exception IOException the source can't be parsed.
+ * @exception CSSException the source is not CSS valid.
+ */
+ public void parseStyleSheet(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+
+ parserUnit();
+ }
+
+ /**
+ * Convenient method for URIs.
+ *
+ * @param systemId the fully resolved URI of the style sheet.
+ * @exception IOException the source can't be parsed.
+ * @exception CSSException the source is not CSS valid.
+ */
+ public void parseStyleSheet(String systemId)
+ throws CSSException, IOException {
+ parseStyleSheet(new InputSource(systemId));
+ }
+
+ /**
+ * This method parses only one rule (style rule or at-rule, except @charset).
+ *
+ * @param source the source of the rule.
+ * @exception IOException the source can't be parsed.
+ * @exception CSSException the source is not CSS valid.
+ */
+ public void parseRule(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+ _parseRule();
+ }
+
+ /**
+ * This method parses a style declaration (including the surrounding curly
+ * braces).
+ *
+ * @param source the source of the style declaration.
+ * @exception IOException the source can't be parsed.
+ * @exception CSSException the source is not CSS valid.
+ */
+ public void parseStyleDeclaration(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+ _parseDeclarationBlock();
+ }
+
+ /**
+ * This methods returns "http://www.w3.org/TR/REC-CSS2".
+ * @return the string "http://www.w3.org/TR/REC-CSS2".
+ */
+ public String getParserVersion() {
+ return "http://www.w3.org/TR/REC-CSS2";
+ }
+
+ /**
+ * Parse methods used by DOM Level 2 implementation.
+ */
+ public void parseImportRule(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+ _parseImportRule();
+ }
+
+ public void parseMediaRule(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+ _parseMediaRule();
+ }
+
+ public SelectorList parseSelectors(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ if (selectorFactory == null) {
+ selectorFactory = new SelectorFactoryImpl();
+ }
+ if (conditionFactory == null) {
+ conditionFactory = new ConditionFactoryImpl();
+ }
+ return _parseSelectors();
+ }
+
+ public LexicalUnit parsePropertyValue(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ return expr();
+ }
+
+ public boolean parsePriority(InputSource source)
+ throws CSSException, IOException {
+ this.source = source;
+ ReInit(getCharStreamWithLurk(source));
+
+ return prio();
+ }
+
+ /**
+ * Convert the source into a Reader. Used only by DOM Level 2 parser methods.
+ */
+ private Reader getReader(InputSource source) throws IOException {
+ if (source.getCharacterStream() != null) {
+ return source.getCharacterStream();
+ } else if (source.getByteStream() != null) {
+ // My DOM level 2 implementation doesn't use this case.
+ if (source.getEncoding() == null) {
+ // unknown encoding, use ASCII as default.
+ return new InputStreamReader(source.getByteStream(), "ASCII");
+ } else {
+ return new InputStreamReader(source.getByteStream(),
+ source.getEncoding());
+ }
+ } else {
+ // systemId
+ // @@TODO
+ throw new CSSException("not yet implemented");
+ }
+ }
+
+ /**
+ * Convert the source into a CharStream with encoding informations.
+ * The encoding can be found in the InputSource or in the CSS document.
+ * Since this method marks the reader and make a reset after looking for
+ * the charset declaration, you'll find the charset declaration into the
+ * stream.
+ */
+ private CharStream getCharStreamWithLurk(InputSource source)
+ throws CSSException, IOException {
+ if (source.getCharacterStream() != null) {
+ // all encoding are supposed to be resolved by the user
+ // return the reader
+ return new Generic_CharStream(source.getCharacterStream(), 1, 1);
+ } else if (source.getByteStream() == null) {
+ // @@CONTINUE ME. see also getReader() with systemId
+ try {
+ source.setByteStream(new URL(source.getURI()).openStream());
+ } catch (Exception e) {
+ try {
+ source.setByteStream(new FileInputStream(source.getURI()));
+ } catch (IOException ex) {
+ throw new CSSException("invalid url ?");
+ }
+ }
+ }
+ String encoding = "ASCII";
+ InputStream input = source.getByteStream();
+ char c = ' ';
+
+ if (!input.markSupported()) {
+ input = new BufferedInputStream(input);
+ source.setByteStream(input);
+ }
+ input.mark(100);
+ c = (char) input.read();
+
+ if (c == '@') {
+ // hum, is it a charset ?
+ int size = 100;
+ byte[] buf = new byte[size];
+ input.read(buf, 0, 7);
+ String keyword = new String(buf, 0, 7);
+ if (keyword.equals("charset")) {
+ // Yes, this is the charset declaration !
+
+ // here I don't use the right declaration : white space are ' '.
+ while ((c = (char) input.read()) == ' ') {
+ // find the first quote
+ }
+ char endChar = c;
+ int i = 0;
+
+ if ((endChar != '"') && (endChar != '\'')) {
+ // hum this is not a quote.
+ throw new CSSException("invalid charset declaration");
+ }
+
+ while ((c = (char) input.read()) != endChar) {
+ buf[i++] = (byte) c;
+ if (i == size) {
+ byte[] old = buf;
+ buf = new byte[size + 100];
+ System.arraycopy(old, 0, buf, 0, size);
+ size += 100;
+ }
+ }
+ while ((c = (char) input.read()) == ' ') {
+ // find the next relevant character
+ }
+ if (c != ';') {
+ // no semi colon at the end ?
+ throw new CSSException("invalid charset declaration: "
+ + "missing semi colon");
+ }
+ encoding = new String(buf, 0, i);
+ if (source.getEncoding() != null) {
+ // compare the two encoding informations.
+ // For example, I don't accept to have ASCII and after UTF-8.
+ // Is it really good ? That is the question.
+ if (!encoding.equals(source.getEncoding())) {
+ throw new CSSException("invalid encoding information.");
+ }
+ }
+ } // else no charset declaration available
+ }
+ // ok set the real encoding of this source.
+ source.setEncoding(encoding);
+ // set the real reader of this source.
+ source.setCharacterStream(new InputStreamReader(source.getByteStream(),
+ Encoding.getJavaEncoding(encoding)));
+ // reset the stream (leave the charset declaration in the stream).
+ input.reset();
+
+ return new Generic_CharStream(source.getCharacterStream(), 1, 1);
+ }
+
+ private LocatorImpl currentLocator;
+ private Locator getLocator() {
+ if (currentLocator == null) {
+ currentLocator = new LocatorImpl(this);
+ return currentLocator;
+ }
+ return currentLocator.reInit(this);
+ }
+ private LocatorImpl getLocator(Token save) {
+ if (currentLocator == null) {
+ currentLocator = new LocatorImpl(this, save);
+ return currentLocator;
+ }
+ return currentLocator.reInit(this, save);
+ }
+
+ private void reportError(Locator l, Exception e) {
+ if (errorHandler != null) {
+ if (e instanceof ParseException) {
+ // construct a clean error message.
+ ParseException pe = (ParseException) e;
+ if (pe.specialConstructor) {
+ StringBuffer errorM = new StringBuffer();
+ if (pe.currentToken != null) {
+ errorM.append("encountered \"")
+ .append(pe.currentToken.next);
+ }
+ errorM.append('"');
+ if (pe.expectedTokenSequences.length != 0) {
+ errorM.append(". Was expecting one of: ");
+ for (int i = 0; i < pe.expectedTokenSequences.length; i++) {
+ for (int j = 0; j < pe.expectedTokenSequences[i].length; j++) {
+ int kind = pe.expectedTokenSequences[i][j];
+ if (kind != S) {
+ errorM.append(pe.tokenImage[kind]);
+ errorM.append(' ');
+ }
+ }
+ }
+ }
+ errorHandler.error(new CSSParseException(errorM.toString(),
+ l, e));
+ } else {
+ errorHandler.error(new CSSParseException(e.getMessage(),
+ l, e));
+ }
+ } else if (e == null) {
+ errorHandler.error(new CSSParseException("error", l, null));
+ } else {
+ errorHandler.error(new CSSParseException(e.getMessage(), l, e));
+ }
+ }
+ }
+
+ private void reportWarningSkipText(Locator l, String text) {
+ if (errorHandler != null && text != null) {
+ errorHandler.warning(new CSSParseException("Skipping: " + text, l));
+ }
+ }
+}
+
+PARSER_END(Parser)
+
+/*
+ * The tokenizer
+ */
+
+<DEFAULT>
+TOKEN :
+{
+ < S : ( [ " ", "\t" , "\n" , "\r", "\f" ] )+ >
+ { image = Parser.SPACE; }
+}
+
+<DEFAULT>
+MORE :
+{
+ "//" : IN_SINGLE_LINE_COMMENT
+}
+
+<IN_SINGLE_LINE_COMMENT>
+MORE :
+{
+ < ~["\n","\r"] > : IN_SINGLE_LINE_COMMENT
+}
+
+<IN_SINGLE_LINE_COMMENT>
+SKIP :
+{
+ < "\n"|"\r"|"\r\n" > : DEFAULT
+}
+
+<DEFAULT>
+MORE :
+{
+ <"/**" ~["/"]> { input_stream.backup(1); } : IN_FORMAL_COMMENT
+|
+ "/*" : IN_MULTI_LINE_COMMENT
+}
+<IN_FORMAL_COMMENT>
+SPECIAL_TOKEN :
+{
+ <FORMAL_COMMENT: "*/" > : DEFAULT
+}
+
+<IN_MULTI_LINE_COMMENT>
+SKIP :
+{
+ <MULTI_LINE_COMMENT: "*/" > : DEFAULT
+}
+
+<IN_FORMAL_COMMENT,IN_MULTI_LINE_COMMENT>
+MORE :
+{
+ < ~[] >
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < CDO : "<!--" >
+ | < CDC : "-->" >
+ | < LBRACE : "{" >
+ | < RBRACE : "}">
+ | < DASHMATCH : "|=" >
+ | < INCLUDES : "~=" >
+ | < EQ : "=" >
+ | < PLUS : "+" >
+ | < MINUS : "-" >
+ | < COMMA : "," >
+ | < SEMICOLON : ";" >
+ | < PRECEDES : ">" >
+ | < DIV : "/" >
+ | < LBRACKET : "[" >
+ | < RBRACKET : "]" >
+ | < ANY : "*" >
+ | < PARENT : "&" >
+ | < DOT : "." >
+ | < LPARAN : "(" >
+ | < RPARAN : ")">
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < COLON : ":" >
+}
+
+< DEFAULT >
+TOKEN :
+{
+ < EACH_VAR : "#{"< VARIABLE > "}">
+}
+
+<DEFAULT>
+TOKEN : /* basic tokens */
+{
+ < NONASCII : ["\200"-"\377"] >
+ | < #H : ["0"-"9", "a"-"f"] >
+ | < #UNICODE : "\\" <H> ( <H> )? /* I can't say {1,6} */
+ ( <H> )? ( <H> )?
+ ( <H> )? ( <H> )?
+ ( [ " ", "\t" , "\n" , "\r", "\f" ] )? >
+ | < #ESCAPE : <UNICODE> | ( "\\" [ " "-"~","\200"-"\377" ] ) >
+ | < #NMSTART : ("-")?[ "a"-"z"] | <NONASCII> | <ESCAPE> >
+ | < #NMCHAR : ["a"-"z", "0"-"9", "-", "_"] | <NONASCII> | <ESCAPE> >
+ | < #STRINGCHAR : [ "\t"," ","!","#","$","%","&","("-"~" ]
+ | "\\\n" | "\\\r\n" | "\\\r" | "\\\f"
+ | <NONASCII> | <ESCAPE> >
+ | < #D : ["0"-"9"] >
+ | < #NAME : ( <NMCHAR> )+ >
+}
+
+
+<DEFAULT>
+TOKEN :
+{
+ <TO : "to">
+ |<THROUGH : "through">
+ |<EACH_IN : "in">
+}
+
+/* DERECTIVES */
+<DEFAULT>
+TOKEN :
+{
+ <MIXIN_SYM : "@mixin">
+ | <INCLUDE_SYM : "@include">
+ | <FUNCTION_SYM : "@function">
+ | <RETURN_SYM : "@return">
+ | <DEBUG_SYM : "@debug">
+ | <WARN_SYM : "@warn">
+ | <FOR_SYM : "@for">
+ | <EACH_SYM : "@each">
+ | <WHILE_SYM : "@while">
+ | <IF_SYM : "@if">
+ | <ELSE_SYM : "@else">
+ | <EXTEND_SYM : "@extend">
+ | <MOZ_DOCUMENT_SYM : "@-moz-document">
+ | <SUPPORTS_SYM : "@supports">
+}
+
+<DEFAULT>
+TOKEN:
+{
+ < GUARDED_SYM : "!" ( <S> )? "default">
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < STRING : ( "\"" ( <STRINGCHAR> | "'" )* "\"" ) |
+ ( "'" ( <STRINGCHAR> | "\"" )* "'" ) >
+ | < IDENT : <NMSTART> ( <NMCHAR> )* >
+ | < NUMBER : ( <D> )+ | ( <D> )* "." ( <D> )+ >
+ | < #_URL : [ "!","#","$","%","&","*"-"~" ] | <NONASCII> | <ESCAPE> >
+ | < URL : "url(" ( <S> )*
+ ( <STRING> | ( <_URL> )* ) ( <S> )* ")" >
+}
+
+<DEFAULT>
+TOKEN:
+{
+ < VARIABLE : "$" <IDENT>>
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < PERCENTAGE : <NUMBER> "%" >
+ | < PT : <NUMBER> "pt" >
+ | < MM : <NUMBER> "mm" >
+ | < CM : <NUMBER> "cm" >
+ | < PC : <NUMBER> "pc" >
+ | < IN : <NUMBER> "in" >
+ | < PX : <NUMBER> "px" >
+ | < EMS : <NUMBER> "em" >
+ | < EXS : <NUMBER> "ex" >
+ | < DEG : <NUMBER> "deg" >
+ | < RAD : <NUMBER> "rad" >
+ | < GRAD : <NUMBER> "grad" >
+ | < MS : <NUMBER> "ms" >
+ | < SECOND : <NUMBER> "s" >
+ | < HZ : <NUMBER> "Hz" >
+ | < KHZ : <NUMBER> "kHz" >
+ | < DIMEN : <NUMBER> <IDENT> >
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < HASH : "#" <NAME> >
+}
+
+/* RESERVED ATRULE WORDS */
+<DEFAULT>
+TOKEN :
+{
+ < IMPORT_SYM : "@import">
+ | < MEDIA_SYM : "@media" >
+ | < CHARSET_SYM : "@charset" >
+ | < PAGE_SYM : "@page" >
+ | < FONT_FACE_SYM: "@font-face" >
+ | < ATKEYWORD : "@" <IDENT> >
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < IMPORTANT_SYM : "!" ( <S> )? "important" >
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < #RANGE0 : <H> <H> <H> <H> <H> >
+ | < #RANGE1 : <H> <H> <H> <H> <H> ( "?" )? >
+ | < #RANGE2 : <H> <H> <H> <H> ( "?" )? ( "?" )? >
+ | < #RANGE3 : <H> <H> <H> ( "?" )? ( "?" )? ( "?" )? >
+ | < #RANGE4 : <H> <H> ( "?" )? ( "?" )? ( "?" )? ( "?" )? >
+ | < #RANGE5 : <H> ( "?" )? ( "?" )? ( "?" )? ( "?" )? ( "?" )? >
+ | < #RANGE6 : "?" ( "?" )? ( "?" )? ( "?" )? ( "?" )? ( "?" )? >
+ | < #RANGE : <RANGE0> | <RANGE1> | <RANGE2>
+ | <RANGE3> | <RANGE4> | <RANGE5> | <RANGE6> >
+ | < #UNI : <H> ( <H> )? ( <H> )? ( <H> )? ( <H> )? ( <H> )? >
+ | < UNICODERANGE : "U+" <RANGE>
+ | "U+" <UNI> "-" <UNI> >
+}
+
+<DEFAULT>
+TOKEN :
+{
+ < FUNCTION : <IDENT>(< S >)* "(" >
+}
+
+<DEFAULT, IN_MULTI_LINE_COMMENT>
+TOKEN :
+{ /* avoid token manager error */
+ < UNKNOWN : ~[] >
+}
+
+/*
+ * The grammar of CSS2
+ */
+
+/**
+ * The main entry for the parser.
+ *
+ * @exception ParseException exception during the parse
+ */
+void parserUnit() :
+{}
+{
+ try {
+ { documentHandler.startDocument(source); }
+ ( charset() )?
+ ( <S> comments()
+ | ignoreStatement() )*
+ ( importDeclaration() ( ignoreStatement() ( <S> )* )* )*
+ afterImportDeclaration()
+ <EOF>
+ } finally {
+ documentHandler.endDocument(source);
+ }
+}
+
+void charset() :
+{ Token n; }
+{
+ try {
+ <CHARSET_SYM> ( <S> )* n=<STRING> ( <S> )* ";"
+ } catch (ParseException e) {
+ reportError(getLocator(e.currentToken.next), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ } catch (Exception e) {
+ reportError(getLocator(), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ }
+}
+
+void afterImportDeclaration() :
+{String ret;
+ Locator l;
+}
+{
+ (
+ ( variable() | mixinDirective()|eachDirective() | includeDirective() | styleRule() | media()| page() | fontFace()
+ | { l = getLocator(); } ret=skipStatement()
+ {
+ if ((ret == null) || (ret.length() == 0)) {
+ return;
+ }
+ reportWarningSkipText(l, ret);
+ if (ret.charAt(0) == '@') {
+ documentHandler.ignorableAtRule(ret);
+ }
+ }
+ )
+ ( ignoreStatement() ( <S> )* )* )*
+}
+
+void ignoreStatement() :
+{}
+{
+ <CDO> | <CDC> | atRuleDeclaration()
+}
+
+/**
+ * The import statement
+ *
+ * @exception ParseException exception during the parse
+ */
+void importDeclaration() :
+{Token n;
+ String uri;
+ MediaListImpl ml = new MediaListImpl();
+ boolean isURL = false;
+}
+{
+ try {
+ <IMPORT_SYM>
+ ( <S> )* ( n=<STRING> { uri = convertStringIndex(n.image, 1,
+ n.image.length() -1); }
+ | n=<URL>
+ {
+ isURL=true;
+ uri = n.image.substring(4, n.image.length()-1).trim();
+ if ((uri.charAt(0) == '"')
+ || (uri.charAt(0) == '\'')) {
+ uri = uri.substring(1, uri.length()-1);
+ }
+ }
+ )
+ ( <S> )* ( mediaStatement(ml) )? ";"
+ ( <S> )*
+ {
+ if (ml.getLength() == 0) {
+ // see section 6.3 of the CSS2 recommandation.
+ ml.addItem("all");
+ }
+ documentHandler.importStyle(uri, ml, isURL);
+ }
+ } catch (ParseException e) {
+ reportError(getLocator(), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+void media() :
+{
+ boolean start = false;
+ String ret;
+ MediaListImpl ml = new MediaListImpl();
+}
+{
+ try {
+ <MEDIA_SYM> ( <S> )*
+ mediaStatement(ml)
+ { start = true; documentHandler.startMedia(ml); }
+ <LBRACE> ( <S> )* ( styleRule() | skipUnknownRule() )* <RBRACE> ( <S> )*
+ } catch (ParseException e) {
+ reportError(getLocator(), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ } finally {
+ if (start) {
+ documentHandler.endMedia(ml);
+ }
+ }
+}
+
+void mediaStatement(MediaListImpl ml) :
+{
+ String m;
+}
+{
+ m=medium() ( <COMMA> ( <S> )* { ml.addItem(m); } m=medium() )*
+ { ml.addItem(m); }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+String medium() : /* tv, projection, screen, ... */
+{Token n;}
+{
+ n=<IDENT> ( <S> )* { return convertIdent(n.image); }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+void page() :
+{
+ boolean start = false;
+ Token n = null;
+ String page = null;
+ String pseudo = null;
+}
+{
+ try {
+ <PAGE_SYM> ( <S> )* ( n=<IDENT> ( <S> )* )?
+ ( pseudo=pseudo_page() )?
+ {
+ if (n != null) {
+ page = convertIdent(n.image);
+ }
+ }
+ <LBRACE> (<S>)*
+ {
+ start = true;
+ documentHandler.startPage(page, pseudo);
+ }
+ ( declaration() )? ( ";" ( <S> )* ( declaration() )? )*
+ <RBRACE> (<S>)*
+ } catch (ParseException e) {
+ if (errorHandler != null) {
+ LocatorImpl li = new LocatorImpl(this,
+ e.currentToken.next.beginLine,
+ e.currentToken.next.beginColumn-1);
+ reportError(li, e);
+ skipStatement();
+ // reportWarningSkipText(li, skipStatement());
+ } else {
+ skipStatement();
+ }
+ } finally {
+ if (start) {
+ documentHandler.endPage(page, pseudo);
+ }
+ }
+}
+
+String pseudo_page() :
+{ Token n; }
+{
+ ":" n=<IDENT> ( <S> )* { return convertIdent(n.image); }
+}
+
+void fontFace() :
+{
+ boolean start = false;
+}
+{
+ try {
+ <FONT_FACE_SYM> ( <S> )*
+ <LBRACE> (<S>)*
+ { start = true; documentHandler.startFontFace(); }
+ ( declaration() )? ( ";" ( <S> )* ( declaration() )? )*
+ <RBRACE> (<S>)*
+ } catch (ParseException e) {
+ reportError(getLocator(), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ } finally {
+ if (start) {
+ documentHandler.endFontFace();
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+void atRuleDeclaration() :
+{Token n;
+ String ret;
+}
+{
+ n=<ATKEYWORD>
+ {
+ ret=skipStatement();
+ reportWarningSkipText(getLocator(), ret);
+ if ((ret != null) && (ret.charAt(0) == '@')) {
+ documentHandler.ignorableAtRule(ret);
+ }
+ }
+}
+
+void skipUnknownRule() :
+{ Token n;}
+{
+ ( n=<ATKEYWORD>
+| n=<CDO>
+| n=<CHARSET_SYM>
+| n=<COMMA>
+| n=<DASHMATCH>
+| n=<FONT_FACE_SYM>
+| n=<FUNCTION>
+| n=<IMPORTANT_SYM>
+| n=<IMPORT_SYM>
+| n=<INCLUDES>
+| n=<LBRACE>
+| n=<MEDIA_SYM>
+| n=<NONASCII>
+| n=<NUMBER>
+| n=<PAGE_SYM>
+| n=<PERCENTAGE>
+| n=<STRING>
+| n=<UNICODERANGE>
+| n=<URL>
+| n=";"
+| n="+"
+| n=">"
+| n="-"
+| n=<UNKNOWN>
+ ) {
+ String ret;
+ Locator loc = getLocator();
+ ret=skipStatement();
+ reportWarningSkipText(loc, ret);
+ if ((ret != null) && (n.image.charAt(0) == '@')) {
+ documentHandler.ignorableAtRule(ret);
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+char combinator() :
+{
+char connector = ' ';
+}
+{
+ "+" ( <S> )* { return '+'; }
+ | ">" ( <S> )* { return '>'; }
+| <S> ( ( "+" { connector = '+'; }
+ | ">" { connector = '>'; } )
+ ( <S> )* )? { return connector; }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+String property() :
+{Token n;
+}
+{
+ n=<IDENT> ( <S> )* { return convertIdent(n.image); }
+}
+
+String variableName() :
+{Token n;}
+{
+ n=<VARIABLE> (<S>)* {return convertIdent(n.image.substring(1));}
+}
+
+String functionName() :
+{Token n;}
+{
+ n=<FUNCTION> ( <S> )* {return convertIdent(n.image.substring(0, n.image.length()-1));}
+}
+/**
+ * @exception ParseException exception during the parse
+ */
+void styleRule() :
+{
+ boolean start = false;
+ SelectorList l = null;
+ Token save;
+ Locator loc;
+}
+{
+ try {
+ l=selectorList() { save = token; } <LBRACE> (<S>)*
+ {
+ start = true;
+ documentHandler.startSelector(l);
+ }
+ ( includeDirective() | media() | extendDirective()| eachDirective() | variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())*
+ <RBRACE> (<S>)*
+ } catch (ThrowedParseException e) {
+ if (errorHandler != null) {
+ LocatorImpl li = new LocatorImpl(this,
+ e.e.currentToken.next.beginLine,
+ e.e.currentToken.next.beginColumn-1);
+ reportError(li, e.e);
+ }
+ } catch (ParseException e) {
+ reportError(getLocator(), e);
+ skipStatement();
+ // reportWarningSkipText(getLocator(), skipStatement());
+ } catch (TokenMgrError e) {
+ reportWarningSkipText(getLocator(), skipStatement());
+ } finally {
+ if (start) {
+ documentHandler.endSelector(l);
+ }
+ }
+}
+
+SelectorList selectorList() :
+{
+ SelectorListImpl selectors = new SelectorListImpl();
+ Selector selector;
+}
+{
+ selector=selector() ( <COMMA> (<S>)* { selectors.addSelector(selector); }
+ selector=selector() )*
+ { selectors.addSelector(selector);
+ return selectors;
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Selector selector() :
+{
+ Selector selector;
+ char comb;
+}
+{
+ try {
+ selector=simple_selector(null, ' ')
+ ( LOOKAHEAD(2) comb=combinator()
+ selector=simple_selector(selector, comb) )* (<S>)*
+ {
+ return selector;
+ }
+ } catch (ParseException e) {
+ /*
+ Token t = getToken(1);
+ StringBuffer s = new StringBuffer();
+ s.append(getToken(0).image);
+ while ((t.kind != COMMA) && (t.kind != SEMICOLON)
+ && (t.kind != LBRACE) && (t.kind != EOF)) {
+ s.append(t.image);
+ getNextToken();
+ t = getToken(1);
+ }
+ reportWarningSkipText(getLocator(), s.toString());
+ */
+ Token t = getToken(1);
+ while ((t.kind != COMMA) && (t.kind != SEMICOLON)
+ && (t.kind != LBRACE) && (t.kind != EOF)) {
+ getNextToken();
+ t = getToken(1);
+ }
+
+ throw new ThrowedParseException(e);
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Selector simple_selector(Selector selector, char comb) :
+{
+ SimpleSelector simple_current = null;
+ Condition cond = null;
+
+ pseudoElt = null;
+}
+{
+ ( simple_current=element_name()
+ ( cond=hash(cond) | cond=_class(cond)
+ | cond=attrib(cond) | cond=pseudo(cond) )*
+ | cond=hash(cond) ( cond=_class(cond)
+ | cond=attrib(cond) | cond=pseudo(cond) )*
+ | cond=_class(cond) ( cond=hash(cond) | cond=_class(cond)
+ | cond=attrib(cond) | cond=pseudo(cond) )*
+ | cond=pseudo(cond) ( cond=hash(cond) | cond=_class(cond)
+ | cond=attrib(cond) | cond=pseudo(cond) )*
+ | cond=attrib(cond) ( cond=hash(cond) | cond=_class(cond)
+ | cond=attrib(cond) | cond=pseudo(cond) )*
+ )
+ {
+ if (simple_current == null) {
+ simple_current = selectorFactory.createElementSelector(null, null);
+ }
+ if (cond != null) {
+ simple_current = selectorFactory.createConditionalSelector(simple_current,
+ cond);
+ }
+ if (selector != null) {
+ switch (comb) {
+ case ' ':
+ selector = selectorFactory.createDescendantSelector(selector,
+ simple_current);
+ break;
+ case '+':
+ selector =
+ selectorFactory.createDirectAdjacentSelector((short) 1,
+ selector,
+ simple_current);
+ break;
+ case '>':
+ selector = selectorFactory.createChildSelector(selector,
+ simple_current);
+ break;
+ default:
+ throw new ParseException("invalid state. send a bug report");
+ }
+ } else {
+ selector= simple_current;
+ }
+ if (pseudoElt != null) {
+ selector = selectorFactory.createChildSelector(selector,
+ selectorFactory.createPseudoElementSelector(null, pseudoElt));
+ }
+ return selector;
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Condition _class(Condition pred) :
+{Token t;
+String s = "";
+Condition c;
+}
+{
+ "." (t = <IDENT>{s += t.image; }|t = < EACH_VAR >{ s += t.image; })+
+ {
+ c = conditionFactory.createClassCondition(null, s);
+ if (pred == null) {
+ return c;
+ } else {
+ return conditionFactory.createAndCondition(pred, c);
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+SimpleSelector element_name() :
+{Token t; String s = "";}
+{
+ (t = <IDENT>{s += t.image; }|t = < EACH_VAR >{ s += t.image; })+
+ {
+ return selectorFactory.createElementSelector(null, s);
+ }
+ | "*"
+ { return selectorFactory.createElementSelector(null, "*"); }
+ | "&"
+ { return selectorFactory.createElementSelector(null, "&"); }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Condition attrib(Condition pred) :
+{
+ int cases = 0;
+ Token att = null;
+ Token val = null;
+ String attValue = null;
+}
+{
+ "[" ( <S> )* att=<IDENT> ( <S> )*
+ ( ( "=" { cases = 1; }
+ | <INCLUDES> { cases = 2; }
+ | <DASHMATCH> { cases = 3; } ) ( <S> )*
+ ( val=<IDENT> { attValue = val.image; }
+ | val=<STRING> { attValue = convertStringIndex(val.image, 1,
+ val.image.length() -1);}
+ )
+ ( <S> )* )?
+ "]"
+ {
+ String name = convertIdent(att.image);
+ Condition c;
+ switch (cases) {
+ case 0:
+ c = conditionFactory.createAttributeCondition(name, null, false, null);
+ break;
+ case 1:
+ c = conditionFactory.createAttributeCondition(name, null, false,
+ attValue);
+ break;
+ case 2:
+ c = conditionFactory.createOneOfAttributeCondition(name, null, false,
+ attValue);
+ break;
+ case 3:
+ c = conditionFactory.createBeginHyphenAttributeCondition(name, null,
+ false,
+ attValue);
+ break;
+ default:
+ // never reached.
+ c = null;
+ }
+ if (pred == null) {
+ return c;
+ } else {
+ return conditionFactory.createAndCondition(pred, c);
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Condition pseudo(Condition pred) :
+{Token n;
+Token language;
+boolean isPseudoElement = false;
+}
+{
+ ":" (":"{isPseudoElement=true;})?( n=<IDENT>
+ {
+ String s = convertIdent(n.image);
+ if (isPseudoElement) {
+ if (pseudoElt != null) {
+ throw new CSSParseException("duplicate pseudo element definition "
+ + s, getLocator());
+ } else {
+ pseudoElt = s;
+ return pred;
+ }
+ } else {
+ Condition c =
+ conditionFactory.createPseudoClassCondition(null, s);
+ if (pred == null) {
+ return c;
+ } else {
+ return conditionFactory.createAndCondition(pred, c);
+ }
+ }
+ }
+ | ( n=<FUNCTION> ( <S> )* language=<IDENT> ( <S> )* ")"
+ {
+ String f = convertIdent(n.image);
+ if (f.equals("lang(")) {
+ Condition d =
+ conditionFactory.createLangCondition(convertIdent(language.image));
+ if (pred == null) {
+ return d;
+ } else {
+ return conditionFactory.createAndCondition(pred, d);
+ }
+ } else {
+ throw new CSSParseException("invalid pseudo function name "
+ + f, getLocator());
+ }
+ }
+ )
+ )
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+Condition hash(Condition pred) :
+{Token n; }
+{
+ n=<HASH>
+ {
+ Condition d =
+ conditionFactory.createIdCondition(n.image.substring(1));
+ if (pred == null) {
+ return d;
+ } else {
+ return conditionFactory.createAndCondition(pred, d);
+ }
+ }
+}
+
+void variable() :
+{
+ String name;
+ LexicalUnit exp = null;
+ boolean guarded = false;
+ String raw;
+}
+{
+ try{
+ name = variableName()
+ ":" ( <S> )* exp=expr() ( guarded=guarded() )?(";"(<S>)*)+
+ //raw=skipStatementUntilSemiColon()
+ {
+ documentHandler.variable(name, exp, guarded);
+ }
+ }catch (JumpException e) {
+ skipAfterExpression();
+ } catch (NumberFormatException e) {
+ if (errorHandler != null) {
+ errorHandler.error(new CSSParseException("Invalid number "
+ + e.getMessage(),
+ getLocator(),
+ e));
+ }
+ reportWarningSkipText(getLocator(), skipAfterExpression());
+ } catch (ParseException e) {
+ if (errorHandler != null) {
+ if (e.currentToken != null) {
+ LocatorImpl li = new LocatorImpl(this,
+ e.currentToken.next.beginLine,
+ e.currentToken.next.beginColumn-1);
+ reportError(li, e);
+ } else {
+ reportError(getLocator(), e);
+ }
+ skipAfterExpression();
+ } else {
+ skipAfterExpression();
+ }
+ }
+}
+
+void eachDirective() :
+{
+ Token var;
+ ArrayList<String> list;
+}
+{
+ < EACH_SYM >
+ (< S >)*
+ var = < VARIABLE > (< S >)* < EACH_IN > (< S >)*
+ list = stringList()
+ < LBRACE >(< S >)*
+ { documentHandler.startEachDirective(var.image, list);}
+ ( includeDirective() | media() | extendDirective()| variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())*
+ < RBRACE >(< S >)*
+ { documentHandler.endEachDirective();}
+}
+
+ArrayList<String > stringList():
+{
+ ArrayList<String > strings = new ArrayList<String >();
+ Token input;
+}
+{
+ (input = < IDENT > (< S >)*)
+ { strings.add(input.image); }
+
+ (< COMMA >(< S >)* input = < IDENT > { strings.add(input.image); } (< S >)*)*
+ { return strings; }
+
+}
+
+void mixinDirective() :
+{
+ String name;
+ ArrayList<VariableNode> args = null;
+ String body;
+}
+{
+ <MIXIN_SYM>
+ (<S>)*
+ (name = property()
+ |(name = functionName()
+ args = arglist()) <RPARAN> (<S>)*) <LBRACE> (<S>)*
+ {documentHandler.startMixinDirective(name, args);}
+ ( includeDirective() | media()| extendDirective()| variable() | LOOKAHEAD(3) declarationOrNestedProperties() | styleRule())*
+ //(includeDirective() | media() | LOOKAHEAD(declaration()) declaration()";"(<S>)* | styleRule())*
+ <RBRACE>(<S>)*
+ {documentHandler.endMixinDirective(name, args);}
+}
+
+ArrayList<VariableNode> arglist() :
+{
+ ArrayList<VariableNode> args = new ArrayList<VariableNode>();
+ VariableNode arg;
+}
+{
+ arg=mixinArg() ( <COMMA> (<S>)* { args.add(arg); }
+ arg=mixinArg() )*
+ { args.add(arg);
+ return args;
+ }
+}
+
+VariableNode mixinArg() :
+{
+ String name;
+ LexicalUnit value = null;
+}
+{
+ name=variableName() (":" (<S>)* value=term(null) )?
+ {
+ VariableNode arg = new VariableNode(name, value, false);
+ return arg;
+ }
+}
+
+ArrayList<LexicalUnit> argValuelist() :
+{
+ ArrayList<LexicalUnit> args = new ArrayList<LexicalUnit>();
+ LexicalUnit argValue;
+}
+{
+ argValue= term(null) { args.add(argValue); }
+ ( <COMMA> (<S>)* argValue = term(null)
+ {args.add(argValue);}
+ )*
+ {return args;}
+}
+
+void includeDirective() :
+{
+ String name;
+ ArrayList<LexicalUnit> args=null;
+}
+{
+ <INCLUDE_SYM>
+ (<S>)*
+ (name = property()
+ |(name = functionName()
+ args = argValuelist()) <RPARAN>)(";"(<S>)*)+
+ {documentHandler.includeDirective(name, args);}
+}
+
+Node functionDirective() :
+{
+ String name;
+ String args = null;
+ String body;
+ int[] stops = {RPARAN};
+}
+{
+ (name=functionName() args = skipStatementUntilRightParan() <RPARAN>)
+ (<S>)*
+ body = skipStatement()
+ {
+ return null;
+ }
+}
+
+Node returnDirective() :
+{
+ String raw;
+}
+{
+ raw = skipStatement()
+ {return null;}
+}
+
+JAVACODE
+void debugDirective(){
+}
+
+JAVACODE
+void warnDirective(){
+}
+
+Node forDirective() :
+{
+ String var;
+ String from;
+ String to;
+ boolean exclusive;
+ String body;
+ Token tok;
+}
+{
+ var = variableName()
+ {
+ int[] toThrough = {TO, THROUGH};
+ from = skipStatementUntil(toThrough);
+ }
+ (tok = <TO> {exclusive = true;}
+ | tok = <THROUGH> {exclusive = false;})
+ to = skipStatementUntilLeftBrace()
+ (<S>)*
+ body = skipStatement()
+ {return documentHandler.forDirective(var, from, to, exclusive, body);}
+}
+
+Node whileDirective() :
+{
+ String condition;
+ String body;
+}
+{
+ condition = skipStatementUntilLeftBrace()
+ body = skipStatement()
+ { return documentHandler.whileDirective(condition, body);}
+}
+
+JAVACODE
+Node ifDirective(){
+ return documentHandler.ifDirective();
+}
+
+JAVACODE
+void elseDirective(){
+ // throw new ParseException("Invalid CSS: @else must come after @if");
+}
+
+void extendDirective() :
+{SelectorList list;}
+{
+ <EXTEND_SYM>
+ (<S>)*
+ list = selectorList()
+ (";"(<S>)*)+
+ {documentHandler.extendDirective(list);}
+}
+
+JAVACODE
+Node importDirective(){
+ return null;
+}
+
+JAVACODE
+Node charsetDirective(){
+ return null;
+}
+
+JAVACODE
+Node mozDocumentDirective(){
+ return null;
+}
+
+JAVACODE
+Node supportsDirective(){
+ return null;
+}
+
+
+void nestedProperties():
+{String name;
+LexicalUnit exp;}
+{
+ name=property()
+ ":" ( <S> )*
+ <LBRACE> (<S>)*
+ {
+ documentHandler.startNestedProperties(name);
+ }
+ ( declaration() )? ( ";" ( <S> )* ( declaration() )? )*
+ <RBRACE>
+ {
+ documentHandler.endNestedProperties(name);
+ }
+ (<S>)*
+}
+/**
+ * @exception ParseException exception during the parse
+ */
+void declarationOrNestedProperties() :
+{ boolean important = false;
+ String name;
+ LexicalUnit exp;
+ Token save;
+ String comment = null;
+}
+{
+ try {
+ name=property()
+ { save = token; }
+ ":" ( <S> )*
+ (exp=expr() ( important=prio() )?
+ {
+ Token next = getToken(1);
+ if(next.kind == SEMICOLON || next.kind == RBRACE){
+ while(next.kind == SEMICOLON){
+ skipStatement();
+ next = getToken(1);
+ }
+ if(token.specialToken!=null){
+ documentHandler.property(name, exp, important, token.specialToken.image);
+ }else{
+ documentHandler.property(name, exp, important, null);
+ }
+ }
+ }
+ |<LBRACE> (<S>)*
+ {
+ documentHandler.startNestedProperties(name);
+ }
+ ( declaration() )? ( ";" ( <S> )* ( declaration() )? )*
+ <RBRACE>(<S>)*
+ {
+ documentHandler.endNestedProperties(name);
+ }
+ )
+
+ } catch (JumpException e) {
+ skipAfterExpression();
+ // reportWarningSkipText(getLocator(), skipAfterExpression());
+ } catch (NumberFormatException e) {
+ if (errorHandler != null) {
+ errorHandler.error(new CSSParseException("Invalid number "
+ + e.getMessage(),
+ getLocator(),
+ e));
+ }
+ reportWarningSkipText(getLocator(), skipAfterExpression());
+ } catch (ParseException e) {
+ if (errorHandler != null) {
+ if (e.currentToken != null) {
+ LocatorImpl li = new LocatorImpl(this,
+ e.currentToken.next.beginLine,
+ e.currentToken.next.beginColumn-1);
+ reportError(li, e);
+ } else {
+ reportError(getLocator(), e);
+ }
+ skipAfterExpression();
+ /*
+ LocatorImpl loc = (LocatorImpl) getLocator();
+ loc.column--;
+ reportWarningSkipText(loc, skipAfterExpression());
+ */
+ } else {
+ skipAfterExpression();
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+void declaration() :
+{ boolean important = false;
+ String name;
+ LexicalUnit exp;
+ Token save;
+}
+{
+ try {
+ name=property()
+ { save = token; }
+ ":" ( <S> )* exp=expr() ( important=prio() )?
+ {
+ documentHandler.property(name, exp, important);
+ }
+ } catch (JumpException e) {
+ skipAfterExpression();
+ // reportWarningSkipText(getLocator(), skipAfterExpression());
+ } catch (NumberFormatException e) {
+ if (errorHandler != null) {
+ errorHandler.error(new CSSParseException("Invalid number "
+ + e.getMessage(),
+ getLocator(),
+ e));
+ }
+ reportWarningSkipText(getLocator(), skipAfterExpression());
+ } catch (ParseException e) {
+ if (errorHandler != null) {
+ if (e.currentToken != null) {
+ LocatorImpl li = new LocatorImpl(this,
+ e.currentToken.next.beginLine,
+ e.currentToken.next.beginColumn-1);
+ reportError(li, e);
+ } else {
+ reportError(getLocator(), e);
+ }
+ skipAfterExpression();
+ /*
+ LocatorImpl loc = (LocatorImpl) getLocator();
+ loc.column--;
+ reportWarningSkipText(loc, skipAfterExpression());
+ */
+ } else {
+ skipAfterExpression();
+ }
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+boolean prio() :
+{}
+{
+ <IMPORTANT_SYM> ( <S> )* { return true; }
+}
+
+boolean guarded() :
+{}
+{
+ <GUARDED_SYM> (<S>)* {return true;}
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+LexicalUnitImpl operator(LexicalUnitImpl prev) :
+{Token n;}
+{
+n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine,
+ n.beginColumn,
+ prev); }
+| n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine,
+ n.beginColumn,
+ prev); }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+LexicalUnit expr() :
+{
+ LexicalUnitImpl first, res;
+ char op;
+}
+{
+ first=term(null){ res = first; }
+ ( LOOKAHEAD(2) ( res=operator(res) )? res=term(res))*
+ { return first; }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+char unaryOperator() :
+{}
+{
+ "-" { return '-'; }
+| "+" { return '+'; }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+LexicalUnitImpl term(LexicalUnitImpl prev) :
+{ LexicalUnitImpl result = null;
+ Token n = null;
+ char op = ' ';
+ String varName;
+}
+{
+ (( ( ( op=unaryOperator() )?
+ (
+ n=<NUMBER>
+ { result = LexicalUnitImpl.createNumber(n.beginLine, n.beginColumn,
+ prev, number(op, n, 0)); }
+ | n=<PERCENTAGE>
+ { result = LexicalUnitImpl.createPercentage(n.beginLine, n.beginColumn,
+ prev, number(op, n, 1)); }
+ | n=<PT>
+ { result = LexicalUnitImpl.createPT(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<CM>
+ { result = LexicalUnitImpl.createCM(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<MM>
+ { result = LexicalUnitImpl.createMM(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<PC>
+ { result = LexicalUnitImpl.createPC(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<IN>
+ { result = LexicalUnitImpl.createIN(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<PX>
+ { result = LexicalUnitImpl.createPX(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<EMS>
+ { result = LexicalUnitImpl.createEMS(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<EXS>
+ { result = LexicalUnitImpl.createEXS(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<DEG>
+ { result = LexicalUnitImpl.createDEG(n.beginLine, n.beginColumn,
+ prev, number(op, n, 3)); }
+ | n=<RAD>
+ { result = LexicalUnitImpl.createRAD(n.beginLine, n.beginColumn,
+ prev, number(op, n, 3)); }
+ | n=<GRAD>
+ { result = LexicalUnitImpl.createGRAD(n.beginLine, n.beginColumn,
+ prev, number(op, n, 3)); }
+ | n=<SECOND>
+ { result = LexicalUnitImpl.createS(n.beginLine, n.beginColumn,
+ prev, number(op, n, 1)); }
+ | n=<MS>
+ { result = LexicalUnitImpl.createMS(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<HZ>
+ { result = LexicalUnitImpl.createHZ(n.beginLine, n.beginColumn,
+ prev, number(op, n, 2)); }
+ | n=<KHZ>
+ { result = LexicalUnitImpl.createKHZ(n.beginLine, n.beginColumn,
+ prev, number(op, n, 3)); }
+ | n=<DIMEN>
+ {
+ String s = n.image;
+ int i = 0;
+ while (i < s.length()
+ && (Character.isDigit(s.charAt(i)) || (s.charAt(i) == '.'))) {
+ i++;
+ }
+ result = LexicalUnitImpl.createDimen(n.beginLine, n.beginColumn, prev,
+ Float.valueOf(s.substring(0, i)).floatValue(),
+ s.substring(i));
+ }
+ | result=function(op, prev) ) )
+ | ( n=<STRING>
+ { result =
+ LexicalUnitImpl.createString(n.beginLine, n.beginColumn, prev,
+ convertStringIndex(n.image, 1,
+ n.image.length() -1));}
+ | n=<IDENT>
+ { String s = convertIdent(n.image);
+ if ("inherit".equals(s)) {
+ result = LexicalUnitImpl.createInherit(n.beginLine, n.beginColumn,
+ prev);
+ } else {
+ result = LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn,
+ prev, convertIdent(n.image));
+ }
+
+ /* /
+ Auto correction code used in the CSS Validator but must not
+ be used by a conformant CSS2 parser.
+ * Common error :
+ * H1 {
+ * color : black
+ * background : white
+ * }
+ *
+ Token t = getToken(1);
+ Token semicolon = new Token();
+ semicolon.kind = SEMICOLON;
+ semicolon.image = ";";
+ if (t.kind == COLON) {
+ // @@SEEME. (generate a warning?)
+ // @@SEEME if expression is a single ident,
+ generate an error ?
+ rejectToken(semicolon);
+
+ result = prev;
+ }
+ / */
+ }
+ | result=hexcolor(prev)
+ | result=url(prev)
+ | result=unicode(prev)
+ ) ) ( <S> )*
+ | varName = variableName()
+ {result = LexicalUnitImpl.createVariable(token.beginLine, token.beginColumn,
+ prev, varName);})
+ {
+ return result;
+ }
+}
+
+/**
+ * Handle all CSS2 functions.
+ * @exception ParseException exception during the parse
+ */
+LexicalUnitImpl function(char operator, LexicalUnitImpl prev) :
+{Token n;
+ LexicalUnit params = null;
+}
+{
+ n=<FUNCTION> ( <S> )*
+ {
+ String fname = convertIdent(n.image);
+ if("alpha(".equals(fname)){
+ String body = skipStatementUntilSemiColon();
+ return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn,
+ null, "alpha("+body);
+ }
+ }
+ ( params=expr() )? ")"
+ {
+ if (operator != ' ') {
+ throw new CSSParseException("invalid operator before a function.",
+ getLocator());
+ }
+ String f = convertIdent(n.image);
+ LexicalUnitImpl l = (LexicalUnitImpl) params;
+ boolean loop = true;
+ if ("rgb(".equals(f)) {
+ // this is a RGB declaration (e.g. rgb(255, 50%, 0) )
+ int i = 0;
+ while (loop && l != null && i < 5) {
+ switch (i) {
+ case 0:
+ case 2:
+ case 4:
+ if ((l.getLexicalUnitType() != LexicalUnit.SAC_INTEGER)
+ && (l.getLexicalUnitType() != LexicalUnit.SAC_PERCENTAGE)) {
+ loop = false;
+ }
+ break;
+ case 1:
+ case 3:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
+ loop = false;
+ }
+ break;
+ default:
+ throw new ParseException("implementation error");
+ }
+ if (loop) {
+ l = (LexicalUnitImpl) l.getNextLexicalUnit();
+ i ++;
+ }
+ }
+ if ((i == 5) && loop && (l == null)) {
+ return LexicalUnitImpl.createRGBColor(n.beginLine,
+ n.beginColumn,
+ prev, params);
+ } else {
+ if (errorHandler != null) {
+ String errorText;
+ Locator loc;
+ if (i < 5) {
+ if (params == null) {
+ loc = new LocatorImpl(this, n.beginLine,
+ n.beginColumn-1);
+ errorText = "not enough parameters.";
+ } else if (l == null) {
+ loc = new LocatorImpl(this, n.beginLine,
+ n.beginColumn-1);
+ errorText = "not enough parameters: "
+ + params.toString();
+ } else {
+ loc = new LocatorImpl(this, l.getLineNumber(),
+ l.getColumnNumber());
+ errorText = "invalid parameter: "
+ + l.toString();
+ }
+ } else {
+ loc = new LocatorImpl(this, l.getLineNumber(),
+ l.getColumnNumber());
+ errorText = "too many parameters: "
+ + l.toString();
+ }
+ errorHandler.error(new CSSParseException(errorText, loc));
+ }
+
+ throw new JumpException();
+ }
+ } else if ("counter".equals(f)) {
+ int i = 0;
+ while (loop && l != null && i < 3) {
+ switch (i) {
+ case 0:
+ case 2:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) {
+ loop = false;
+ }
+ break;
+ case 1:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
+ loop = false;
+ }
+ break;
+ default:
+ throw new ParseException("implementation error");
+ }
+ l = (LexicalUnitImpl) l.getNextLexicalUnit();
+ i ++;
+ }
+ if (((i == 1) || (i == 3)) && loop && (l == null)) {
+ return LexicalUnitImpl.createCounter(n.beginLine, n.beginColumn,
+ prev, params);
+ }
+
+ } else if ("counters(".equals(f)) {
+
+ int i = 0;
+ while (loop && l != null && i < 5) {
+ switch (i) {
+ case 0:
+ case 4:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) {
+ loop = false;
+ }
+ break;
+ case 2:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_STRING_VALUE) {
+ loop = false;
+ }
+ break;
+ case 1:
+ case 3:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
+ loop = false;
+ }
+ break;
+ default:
+ throw new ParseException("implementation error");
+ }
+ l = (LexicalUnitImpl) l.getNextLexicalUnit();
+ i ++;
+ }
+ if (((i == 3) || (i == 5)) && loop && (l == null)) {
+ return LexicalUnitImpl.createCounters(n.beginLine, n.beginColumn,
+ prev, params);
+ }
+ } else if ("attr(".equals(f)) {
+ if ((l != null)
+ && (l.getNextLexicalUnit() == null)
+ && (l.getLexicalUnitType() == LexicalUnit.SAC_IDENT)) {
+ return LexicalUnitImpl.createAttr(l.getLineNumber(),
+ l.getColumnNumber(),
+ prev, l.getStringValue());
+ }
+ } else if ("rect(".equals(f)) {
+ int i = 0;
+ while (loop && l != null && i < 7) {
+ switch (i) {
+ case 0:
+ case 2:
+ case 4:
+ case 6:
+ switch (l.getLexicalUnitType()) {
+ case LexicalUnit.SAC_INTEGER:
+ if (l.getIntegerValue() != 0) {
+ loop = false;
+ }
+ break;
+ case LexicalUnit.SAC_IDENT:
+ if (!l.getStringValue().equals("auto")) {
+ loop = false;
+ }
+ break;
+ case LexicalUnit.SAC_EM:
+ case LexicalUnit.SAC_EX:
+ case LexicalUnit.SAC_PIXEL:
+ case LexicalUnit.SAC_CENTIMETER:
+ case LexicalUnit.SAC_MILLIMETER:
+ case LexicalUnit.SAC_INCH:
+ case LexicalUnit.SAC_POINT:
+ case LexicalUnit.SAC_PICA:
+ // nothing
+ break;
+ default:
+ loop = false;
+ }
+ break;
+ case 1:
+ case 3:
+ case 5:
+ if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) {
+ loop = false;
+ }
+ break;
+ default:
+ throw new ParseException("implementation error");
+ }
+ l = (LexicalUnitImpl) l.getNextLexicalUnit();
+ i ++;
+ }
+ if ((i == 7) && loop && (l == null)) {
+ return LexicalUnitImpl.createRect(n.beginLine, n.beginColumn,
+ prev, params);
+ }
+ }
+ return LexicalUnitImpl.createFunction(n.beginLine, n.beginColumn, prev,
+ f.substring(0,
+ f.length() -1),
+ params);
+ }
+}
+
+LexicalUnitImpl unicode(LexicalUnitImpl prev) :
+{ Token n;
+}
+{
+ n=<UNICODERANGE>
+ {
+ LexicalUnitImpl params = null;
+ String s = n.image.substring(2);
+ int index = s.indexOf('-');
+ if (index == -1) {
+ params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn,
+ params, Integer.parseInt(s, 16));
+ } else {
+ String s1 = s.substring(0, index);
+ String s2 = s.substring(index);
+
+ params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn,
+ params, Integer.parseInt(s1, 16));
+ params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn,
+ params, Integer.parseInt(s2, 16));
+ }
+
+ return LexicalUnitImpl.createUnicodeRange(n.beginLine, n.beginColumn,
+ prev, params);
+ }
+}
+
+LexicalUnitImpl url(LexicalUnitImpl prev) :
+{ Token n;
+}
+{
+ n=<URL>
+ {
+ String urlname = n.image.substring(4, n.image.length()-1).trim();
+ if (urlname.charAt(0) == '"'
+ || urlname.charAt(0) == '\'') {
+ urlname = urlname.substring(1, urlname.length()-1);
+ }
+ return LexicalUnitImpl.createURL(n.beginLine, n.beginColumn, prev, urlname);
+ }
+}
+
+/**
+ * @exception ParseException exception during the parse
+ */
+LexicalUnitImpl hexcolor(LexicalUnitImpl prev) :
+{Token n;
+}
+{
+ n=<HASH>
+ {
+ int r;
+ LexicalUnitImpl first, params = null;
+ String s = n.image.substring(1);
+
+ if(s.length()!=3 && s.length()!=6) {
+ first = null;
+ throw new CSSParseException("invalid hexadecimal notation for RGB: " + s,
+ getLocator());
+ }
+ return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn,
+ prev, n.image);
+ }
+}
+
+JAVACODE
+float number(char operator, Token n, int lengthUnit) {
+ String image = n.image;
+ float f = 0;
+
+ if (lengthUnit != 0) {
+ image = image.substring(0, image.length() - lengthUnit);
+ }
+ f = Float.valueOf(image).floatValue();
+ return (operator == '-')? -f: f;
+}
+
+JAVACODE
+String skipStatementUntilSemiColon(){
+ int[] semicolon = {SEMICOLON};
+ return skipStatementUntil(semicolon);
+}
+
+JAVACODE
+String skipStatementUntilLeftBrace(){
+ int[] lBrace = {LBRACE};
+ return skipStatementUntil(lBrace);
+}
+
+JAVACODE
+String skipStatementUntilRightParan(){
+ int[] rParan = {RPARAN};
+ return skipStatementUntil(rParan);
+}
+
+JAVACODE
+String skipStatementUntil(int[] symbols){
+ StringBuffer s = new StringBuffer();
+ boolean stop = false;
+ Token tok;
+ while(!stop){
+ tok = getToken(1);
+ if(tok.kind == EOF) {
+ return null;
+ }
+ for(int sym : symbols){
+ if(tok.kind == sym){
+ stop = true;
+ break;
+ }
+ }
+ if(!stop){
+ if (tok.image != null) {
+ s.append(tok.image);
+ }
+ getNextToken();
+ }
+ }
+ return s.toString().trim();
+}
+
+
+JAVACODE
+String skipStatement() {
+ StringBuffer s = new StringBuffer();
+ Token tok = getToken(0);
+ if (tok.image != null) {
+ s.append(tok.image);
+ }
+ while (true) {
+ tok = getToken(1);
+ if (tok.kind == EOF) {
+ return null;
+ }
+ s.append(tok.image);
+ if (tok.kind == LBRACE) {
+ getNextToken();
+ s.append(skip_to_matching_brace());
+ getNextToken();
+ tok = getToken(1);
+ break;
+ } else if (tok.kind == RBRACE) {
+ getNextToken();
+ tok = getToken(1);
+ break;
+ } else if (tok.kind == SEMICOLON) {
+ getNextToken();
+ tok = getToken(1);
+ break;
+ }
+ getNextToken();
+ }
+
+ // skip white space
+ while (true) {
+ if (tok.kind != S) {
+ break;
+ }
+ tok = getNextToken();
+ tok = getToken(1);
+ }
+
+ return s.toString().trim();
+}
+
+JAVACODE
+String skip_to_matching_brace() {
+ StringBuffer s = new StringBuffer();
+ Token tok;
+ int nesting = 1;
+ while (true) {
+ tok = getToken(1);
+ if (tok.kind == EOF) {
+ break;
+ }
+ s.append(tok.image);
+ if (tok.kind == LBRACE) {
+ nesting++;
+ } else if (tok.kind == RBRACE) {
+ nesting--;
+ if (nesting == 0) {
+ break;
+ }
+ }
+ getNextToken();
+ }
+ return s.toString();
+}
+
+/*
+ * Here I handle all CSS2 unicode character stuffs.
+ * I convert all \XXXXXX character into a single character.
+ * Don't forget that the parser has recognize the token before.
+ * (So IDENT won't contain newline and stuffs like this).
+ */
+JAVACODE
+String convertStringIndex(String s, int start, int len) {
+ StringBuffer buf = new StringBuffer(len);
+ int index = start;
+
+ while (index < len) {
+ char c = s.charAt(index);
+ if (c == '\\') {
+ if (++index < len) {
+ c = s.charAt(index);
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+ case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+ int numValue = Character.digit(c, 16);
+ int count = 0;
+ int p = 16;
+
+ while (index + 1 < len && count < 6) {
+ c = s.charAt(index+1);
+
+ if (Character.digit(c, 16) != -1) {
+ numValue = (numValue * 16) + Character.digit(c, 16);
+ p *= 16;
+ index++;
+ } else {
+ if (c == ' ') {
+ // skip the latest white space
+ index++;
+ }
+ break;
+ }
+ }
+ buf.append((char) numValue);
+ break;
+ case '\n':
+ case '\f':
+ break;
+ case '\r':
+ if (index + 1 < len) {
+ if (s.charAt(index + 1) == '\n') {
+ index ++;
+ }
+ }
+ break;
+ default:
+ buf.append(c);
+ }
+ } else {
+ throw new CSSParseException("invalid string " + s, getLocator());
+ }
+ } else {
+ buf.append(c);
+ }
+ index++;
+ }
+
+ return buf.toString();
+}
+
+JAVACODE
+String convertIdent(String s) {
+ return convertStringIndex(s, 0, s.length());
+}
+
+JAVACODE
+String convertString(String s) {
+ return convertStringIndex(s, 0, s.length());
+}
+
+JAVACODE
+void comments(){
+ if (token.specialToken != null){
+ Token tmp_t = token.specialToken;
+ while (tmp_t.specialToken != null) tmp_t = tmp_t.specialToken;
+ while (tmp_t != null) {
+ documentHandler.comment(tmp_t.image);
+ tmp_t = tmp_t.next;
+ }
+ }
+}
+
+/*
+ * @@HACK
+ * I can't insert a token into the tokens flow.
+ * It's jj_consume_token implementation dependant! :-(
+ */
+JAVACODE
+void rejectToken(Token t) {
+ Token fakeToken = new Token();
+ t.next = token;
+ fakeToken.next = t;
+ token = fakeToken;
+}
+
+/**
+ * skip after an expression
+ */
+JAVACODE
+String skipAfterExpression() {
+ Token t = getToken(1);
+ StringBuffer s = new StringBuffer();
+ s.append(getToken(0).image);
+
+ while ((t.kind != RBRACE) && (t.kind != SEMICOLON) && (t.kind != EOF)) {
+ s.append(t.image);
+ getNextToken();
+ t = getToken(1);
+ }
+
+ return s.toString();
+}
+
+/**
+ * The following functions are useful for a DOM CSS implementation only and are
+ * not part of the general CSS2 parser.
+ */
+
+void _parseRule() :
+{String ret = null;
+}
+{
+ ( <S> )*
+ ( importDeclaration() | styleRule() | media() | page()
+ | fontFace() | ret=skipStatement()
+ {
+ if ((ret == null) || (ret.length() == 0)) {
+ return;
+ }
+ if (ret.charAt(0) == '@') {
+ documentHandler.ignorableAtRule(ret);
+ } else {
+ throw new CSSParseException("unrecognize rule: " + ret,
+ getLocator());
+ }
+ }
+ )
+}
+
+void _parseImportRule() :
+{
+}
+{
+ ( <S> )* importDeclaration()
+}
+
+void _parseMediaRule() :
+{
+}
+{
+ ( <S> )* media()
+}
+
+void _parseDeclarationBlock() :
+{
+}
+{
+ ( <S> )*
+ ( declaration() )? ( ";" ( <S> )* ( declaration() )? )*
+ }
+
+SelectorList _parseSelectors() :
+{ SelectorList p = null;
+}
+{
+ try {
+ ( <S> )* p = selectorList()
+ { return p; }
+ } catch (ThrowedParseException e) {
+ throw (ParseException) e.e.fillInStackTrace();
+ }
+}
+
+/*
+ * Local Variables:
+ * compile-command: javacc Parser.jj & javac Parser.java
+ * End:
+ */