Created
June 4, 2019 15:49
-
-
Save niloc132/91ef3a7573f896323eeb82d7123282f5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* -*-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 org.w3c.flute.parser; | |
import java.io.*; | |
import java.net.*; | |
import java.util.Locale; | |
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; | |
/** | |
* 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 StringBuffer SPACE = new StringBuffer(" "); | |
// the document handler for the parser | |
protected DocumentHandler 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 = 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 : /* Comments */ | |
{ | |
< "/*" > : IN_COMMENT | |
} | |
<IN_COMMENT> | |
SKIP : | |
{ | |
< "*/" > : DEFAULT | |
} | |
<IN_COMMENT> | |
MORE : | |
{ | |
< ~[] > : IN_COMMENT | |
} | |
<DEFAULT> | |
TOKEN : | |
{ | |
< CDO : "<!--" > | |
| < CDC : "-->" > | |
| < LBRACE : "{" > | |
| < RBRACE : "}"> | |
| < DASHMATCH : "|=" > | |
| < INCLUDES : "~=" > | |
| < EQ : "=" > | |
| < PLUS : "+" > | |
| < MINUS : "-" > | |
| < COMMA : "," > | |
| < SEMICOLON : ";" > | |
| < PRECEDES : ">" > | |
| < DIV : "/" > | |
| < LBRACKET : "[" > | |
| < RBRACKET : "]" > | |
| < ANY : "*" > | |
| < DOT : "." > | |
| < LPARAN : ")" > | |
| < RPARAN : "("> | |
} | |
<DEFAULT> | |
TOKEN : | |
{ | |
< COLON : ":" > | |
} | |
<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 : | |
{ | |
< STRING : ( "\"" ( <STRINGCHAR> | "'" )* "\"" ) | | |
( "'" ( <STRINGCHAR> | "\"" )* "'" ) > | |
| < IDENT : ("-")? <NMSTART> ( <NMCHAR> )* > | |
| < NUMBER : ( <D> )+ | ( <D> )* "." ( <D> )+ > | |
| < #_URL : [ "!","#","$","%","&","*"-"~" ] | <NONASCII> | <ESCAPE> > | |
| < URL : "url(" ( <S> )* | |
( <STRING> | ( <_URL> )* ) ( <S> )* ")" > | |
} | |
<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> "(" > | |
} | |
<DEFAULT, IN_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> | 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; | |
} | |
{ | |
( ( 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(); | |
} | |
{ | |
try { | |
<IMPORT_SYM> | |
( <S> )* ( n=<STRING> { uri = convertStringIndex(n.image, 1, | |
n.image.length() -1); } | |
| n=<URL> | |
{ | |
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, null); | |
} | |
} 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); } | |
} | |
/** | |
* @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); | |
} | |
( declaration() )? ( ";" ( <S> )* ( declaration() )? )* | |
<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 n; | |
Condition c; | |
} | |
{ | |
"." n=<IDENT> | |
{ | |
c = conditionFactory.createClassCondition(null, n.image); | |
if (pred == null) { | |
return c; | |
} else { | |
return conditionFactory.createAndCondition(pred, c); | |
} | |
} | |
} | |
/** | |
* @exception ParseException exception during the parse | |
*/ | |
SimpleSelector element_name() : | |
{Token n; } | |
{ | |
n=<IDENT> | |
{ | |
return selectorFactory.createElementSelector(null, convertIdent(n.image)); | |
} | |
| "*" | |
{ return selectorFactory.createElementSelector(null, 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 t1 = null; | |
Token language; | |
} | |
{ | |
":" [(t1 = <COLON>)] ( n=<IDENT> | |
{ | |
String prefix = (t1 != null) ? ":" : ""; | |
String s = prefix + convertIdent(n.image); | |
if (s.equals("first-letter") || s.equals("first-line")) { | |
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); | |
} | |
} | |
} | |
/** | |
* @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; } | |
} | |
/** | |
* @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; } | |
( ( 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 = ' '; | |
} | |
{ | |
( ( ( 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> )* | |
{ | |
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> )* ( 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) { | |
String sh = s.substring(0,1); | |
r = Integer.parseInt(sh+sh, 16); | |
first = params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, | |
params, r); | |
params = LexicalUnitImpl.createComma(n.beginLine, n.beginColumn, | |
params); | |
sh = s.substring(1,2); | |
r = Integer.parseInt(sh+sh, 16); | |
params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, | |
params, r); | |
params = LexicalUnitImpl.createComma(n.beginLine, n.beginColumn, | |
params); | |
sh = s.substring(2,3); | |
r = Integer.parseInt(sh+sh, 16); | |
params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, | |
params, r); | |
} else if (s.length() == 6) { | |
r = Integer.parseInt(s.substring(0,2), 16); | |
first = params = LexicalUnitImpl.createInteger(n.beginLine, | |
n.beginColumn, | |
params, r); | |
params = LexicalUnitImpl.createComma(n.beginLine, n.beginColumn, | |
params); | |
r = Integer.parseInt(s.substring(2,4), 16); | |
params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, | |
params, r); | |
params = LexicalUnitImpl.createComma(n.beginLine, n.beginColumn, | |
params); | |
r = Integer.parseInt(s.substring(4,6), 16); | |
params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, | |
params, r); | |
} else { | |
first = null; | |
throw new CSSParseException("invalid hexadecimal notation for RGB: " + s, | |
getLocator()); | |
} | |
return LexicalUnitImpl.createRGBColor(n.beginLine, n.beginColumn, | |
prev, first); | |
} | |
} | |
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 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()); | |
} | |
/* | |
* @@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: | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment