w3c.css.parser.CssFouffa Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cssvalidator Show documentation
Show all versions of cssvalidator Show documentation
Backend for the W3C CSS Validation Service
//
// $Id$
// From Philippe Le Hegaret ([email protected])
//
// (c) COPYRIGHT MIT, ERCIM and Keio, 2003.
// Please first read the full copyright statement in file COPYRIGHT.html
/*
This class is the front end of the CSS parser
*/
package org.w3c.css.parser;
import org.w3c.css.atrules.css.AtRuleImport;
import org.w3c.css.atrules.css.AtRuleMedia;
import org.w3c.css.atrules.css.AtRuleNamespace;
import org.w3c.css.atrules.css.media.MediaFeature;
import org.w3c.css.css.StyleSheetOrigin;
import org.w3c.css.parser.analyzer.CssParser;
import org.w3c.css.parser.analyzer.CssParserTokenManager;
import org.w3c.css.parser.analyzer.ParseException;
import org.w3c.css.parser.analyzer.TokenMgrError;
import org.w3c.css.properties.PropertiesLoader;
import org.w3c.css.properties.css.CssProperty;
import org.w3c.css.util.ApplContext;
import org.w3c.css.util.CssVersion;
import org.w3c.css.util.HTTPURL;
import org.w3c.css.util.InvalidParamException;
import org.w3c.css.util.Util;
import org.w3c.css.util.WarningParamException;
import org.w3c.css.util.Warnings;
import org.w3c.css.values.CssExpression;
import org.w3c.css.values.CssValue;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.util.ArrayList;
/**
* This class is a front end of the CSS1 parser.
*
*
* Example:
*
* CssFouffa parser =
* new CssFouffa(new URL("http://www.w3.org/drafts.css"));
* CssValidatorListener myListener = new MyParserListener();
*
* parser.addListener(myListener);
* parser.parseStyle();
*
*
* @version $Revision$
*/
public final class CssFouffa extends CssParser {
// all properties
CssPropertyFactory properties = null;
// all listeners
ArrayList listeners;
// all errors
Errors errors;
// origin of the style sheet
int origin;
ArrayList visited = null;
/**
* Create a new CssFouffa with a data input and a begin line number.
*
* @param ac The validation context
* @param reader The data stream reader
* @param file The source file (use for errors, warnings and import)
* @param beginLine The begin line number in the file. (used for HTML for example)
* @throws IOException if an I/O error occurs.
*/
public CssFouffa(ApplContext ac, Reader reader, URL file, int beginLine)
throws IOException {
super(reader);
if (ac.getOrigin() == -1) {
setOrigin(StyleSheetOrigin.AUTHOR); // default is user
} else {
setOrigin(ac.getOrigin()); // default is user
}
ac.setFrame(new Frame(this, file.toString(), beginLine,
ac.getWarningLevel()));
setApplContext(ac);
// @@this is a default media ...
/*
* AtRuleMedia media = new AtRuleMedia();
*
* if (ac.getMedium() == null) { try { media.addMedia("all", ac); }
* catch (InvalidParamException e) {} //ignore } else { try {
* media.addMedia(ac.getMedium(), ac); } catch (Exception e) {
* System.err.println(e.getMessage()); try { media.addMedia("all", ac); }
* catch (InvalidParamException ex) {} //ignore } } setAtRule(media);
*/
setURL(file);
if (Util.onDebug) {
System.err.println("[DEBUG] CSS version " + ac.getCssVersionString() +
" medium " + ac.getMedium() + " at-rule "
+ getAtRule() + " profile " + ac.getProfileString());
}
// load the CssStyle
String spec = ac.getPropertyKey();
String classStyle;
classStyle = PropertiesLoader.config.getProperty(spec);
if (classStyle == null) {
spec = CssVersion.getDefault().toString();
classStyle = PropertiesLoader.config.getProperty(spec);
}
Class style;
try {
style = Class.forName(classStyle);
ac.setCssSelectorsStyle(style);
} catch (ClassNotFoundException e) {
System.err.println("org.w3c.css.parser.CssFouffa: couldn't" +
" load the style");
e.printStackTrace();
}
properties = new CssPropertyFactory(spec);
listeners = new ArrayList();
}
/**
* Create a new CssFouffa with a data input and a begin line number.
*
* @param input data input
* @param file The source file (use for errors, warnings and import)
* @param beginLine The begin line number in the file. (used for HTML for example)
* @throws IOException if an I/O error occurs.
*/
public CssFouffa(ApplContext ac, InputStream input, String charset,
URL file, int beginLine)
throws IOException {
this(ac, new InputStreamReader(input, (charset == null) ?
"iso-8859-1" : charset), file, beginLine);
}
/**
* Create a new CssFouffa with a data input.
*
* @param input data input
* @param file The source file (use for errors, warnings and import)
* @throws IOException if an I/O error occurs.
*/
public CssFouffa(ApplContext ac, InputStream input, URL file)
throws IOException {
this(ac, input, (ac.getCharsetForURL(file) != null) ?
ac.getCharsetForURL(file) : "iso-8859-1", file, 0);
}
/**
* Create a new CssFouffa.
*
* @param file The source file (use for data input, errors, warnings and
* import)
* @throws IOException if an I/O error occurs.
*/
public CssFouffa(ApplContext ac, URL file) throws IOException {
this(ac, HTTPURL.getConnection(file, ac));
}
/**
* Create a new CssFouffa. internal, to get the URLCOnnection and fill the
* URL with the relevant one
*/
private CssFouffa(ApplContext ac, URLConnection uco) throws IOException {
this(ac, HTTPURL.getInputStream(ac, uco),
HTTPURL.getCharacterEncoding(ac, uco), uco.getURL(), 0);
String httpCL = uco.getHeaderField("Content-Location");
if (httpCL != null) {
setURL(HTTPURL.getURL(getURL(), httpCL));
}
}
/**
* Create a new CssFouffa. Used by handleImport.
*
* @param in The source input stream (use for data input, errors,
* warnings and import)
* @param listeners Works with this listeners
* @throws IOException if an I/O error occurs.
*/
private CssFouffa(ApplContext ac, InputStream in, URL url,
ArrayList listeners,
ArrayList urlvisited,
CssPropertyFactory cssfactory, boolean mode)
throws IOException {
this(ac, in, ac.getCharsetForURL(url), url, 0);
this.visited = urlvisited;
setURL(url);
ac.setFrame(new Frame(this, url.toString(), ac.getWarningLevel()));
setApplContext(ac);
this.listeners = listeners;
this.properties = cssfactory;
this.mode = mode;
}
private void ReInit(ApplContext ac, InputStream input,
URL file, Frame frame) {
// reinitialize the parser with a new data input
// and a new frame for errors and warnings
super.ReInitWithAc(input, ac, ac.getCharsetForURL(file));
// @@this is a default media ...
// AtRuleMedia media;
// if ("css1".equals(ac.getCssVersionString())) {
// media = new AtRuleMediaCSS1();
// } else if ("css2".equals(ac.getCssVersionString())) {
// media = new AtRuleMedia();
// } else {
// media = new AtRuleMedia();
// }
/*
* if (ac.getMedium() == null) { try { media.addMedia("all", ac); }
* catch (InvalidParamException e) {} //ignore } else { try {
* media.addMedia(ac.getMedium(), ac); } catch (Exception e) {
* System.err.println(e.getMessage()); try { media.addMedia("all", ac); }
* catch (InvalidParamException ex) {} //ignore } } setAtRule(media);
*/
setURL(file);
if (Util.onDebug) {
System.err.println("[DEBUG] CSS version " + ac.getCssVersionString() + " medium " + ac.getMedium() + " profile "
+ ac.getProfileString());
}
String spec = ac.getPropertyKey();
// load the CssStyle
String classStyle = PropertiesLoader.config.getProperty(spec);
if (classStyle == null) {
spec = CssVersion.getDefault().toString();
classStyle = PropertiesLoader.config.getProperty(spec);
}
Class style;
try {
style = Class.forName(classStyle);
ac.setCssSelectorsStyle(style);
} catch (ClassNotFoundException e) {
System.err.println("org.w3c.css.parser.CssFouffa: couldn't" + " load the style");
e.printStackTrace();
}
properties = new CssPropertyFactory(spec);
// loadConfig(ac.getCssVersionString(), ac.getProfileString());
}
/**
* Reinitializes a new CssFouffa with a data input and a begin line number.
*
* @param input data input
* @param file The source file (use for errors, warnings and import)
* @param beginLine The begin line number in the file. (used for HTML for example)
* @throws IOException if an I/O error occurs.
*/
public void ReInit(ApplContext ac, InputStream input, URL file,
int beginLine)
throws IOException {
Frame f = new Frame(this, file.toString(), beginLine,
ac.getWarningLevel());
ac.setFrame(f);
ReInit(ac, input, file, f);
}
/**
* Reinitializes a new CssFouffa with a data input.
*
* @param input data input
* @param file The source file (use for errors, warnings and import)
* @throws IOException if an I/O error occurs.
*/
public void ReInit(ApplContext ac, InputStream input, URL file)
throws IOException {
Frame f = new Frame(this, file.toString(), ac.getWarningLevel());
ac.setFrame(f);
ReInit(ac, input, file, f);
}
/**
* Reinitializes a new CssFouffa.
*
* @param file The source file (use for data input, errors, warnings and
* import)
* @throws IOException if an I/O error occurs.
*/
public void ReInit(ApplContext ac, URL file) throws IOException {
InputStream is;
URL url;
Frame f;
f = new Frame(this, file.toString(), ac.getWarningLevel());
ac.setFrame(f);
if (ac.isInputFake()) {
is = ac.getFakeInputStream(file);
url = file;
} else {
URLConnection urlC = HTTPURL.getConnection(file, ac);
is = HTTPURL.getInputStream(ac, urlC);
url = urlC.getURL();
}
ReInit(ac, is, url, f);
}
/**
* Set the attribute origin
*
* @param origin the new value for the attribute
*/
private final void setOrigin(int origin) {
this.origin = origin;
}
/**
* Returns the attribute origin
*
* @return the value of the attribute
*/
public final int getOrigin() {
return origin;
}
/**
* Adds a listener to the parser.
*
* @param listener The listener
* @see org.w3c.css.parser.CssValidatorListener
*/
public final void addListener(CssValidatorListener listener) {
listeners.add(listener);
}
/**
* Removes a listener from the parser
*
* @param listener The listener
* @see org.w3c.css.parser.CssValidatorListener
*/
public final void removeListener(CssValidatorListener listener) {
listeners.remove(listener);
}
/**
* Parse the style sheet. This is the main function of this parser.
*
*
* Example:
*
* CssFouffa parser = new CssFouffa(new
* URL("http://www.w3.org/drafts.css"));
* CssValidatorListener myListener = new MyParserListener();
*
* parser.addListener(myListener);
* parser.parseStyle();
*
*
* @see org.w3c.css.parser.CssFouffa#addListener
*/
public void parseStyle() {
try {
parserUnit();
} catch (TokenMgrError e) {
throw e;
} catch (Throwable e) {
if (Util.onDebug) {
e.printStackTrace();
}
RuntimeException ne = new RuntimeException(e.getMessage());
ne.fillInStackTrace();
throw (ne);
}
Errors errors = ac.getFrame().getErrors();
Warnings warnings = ac.getFrame().getWarnings();
// That's all folks, notify all errors and warnings
for (CssValidatorListener listener : listeners) {
listener.notifyErrors(errors);
listener.notifyWarnings(warnings);
}
}
/**
* Call the namespace declaration statement
*
* @param url, the style sheet where this declaration statement appears.
* @param prefix, the namespace prefix
* @param nsname, the file/url name in the declaration statement
* @param is_url, if the nsname is a file or an url
*/
public void handleNamespaceDeclaration(URL url, String prefix,
String nsname,
boolean is_url) {
AtRuleNamespace nsrule = new AtRuleNamespace(prefix, nsname, is_url);
newAtRule(nsrule);
endOfAtRule();
// add the NS in the global context definition
ac.setNamespace(url, prefix, nsname);
}
/**
* Call by the import statement.
*
* @param url The style sheet where this import statement appears.
* @param file the file name in the import statement
*/
public void handleImport(URL url, String file, boolean is_url,
AtRuleMedia media) {
// CssError cssError = null;
AtRuleImport importrule = new AtRuleImport(file, is_url, media);
newAtRule(importrule);
endOfAtRule();
URL importedURL;
try {
importedURL = HTTPURL.getURL(url, file);
} catch (MalformedURLException mue) {
if (!Util.noErrorTrace) {
ac.getFrame()
.addError(new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(),
mue));
}
return;
}
// add it to the list of linked URIs
// only if it was a string (otherwise the URI is already there
// as CssURL contains code to add it directly
if (!is_url) {
ac.addLinkedURI(importedURL);
}
// check if we need to follow it
if (!ac.followlinks()) {
// TODO add a warning ?
return;
}
//if it's not permitted to import... (direct input)
if (url.getProtocol().equals("file")) {
ac.getFrame().addWarning("unsupported-import");
return;
}
try {
String surl = importedURL.toString();
if (visited == null) {
visited = new ArrayList();
} else {
// check that we didn't already got this URL, or that the
// number of imports is not exploding
if (visited.contains(surl)) {
CssError cerr = new CssError(getSourceFile(),
getBeginLine(), getBeginColumn(), getEndLine(),
getEndColumn(), new Exception(
"Import loop" + " detected in " + surl));
ac.getFrame().addError(cerr);
return;
} else if (visited.size() > 42) {
CssError cerr = new CssError(getSourceFile(),
getBeginLine(), getBeginColumn(), getEndLine(),
getEndColumn(), new Exception("Maximum number"
+ " of imports " + "reached"));
ac.getFrame().addError(cerr);
return;
}
}
ArrayList newVisited = new ArrayList(visited);
newVisited.add(surl);
if (Util.importSecurity) {
throw new FileNotFoundException("[SECURITY] You can't " +
"import URL sorry.");
}
URLConnection importURL = HTTPURL.getConnection(importedURL, ac);
String charset = HTTPURL.getCharacterEncoding(ac, importURL);
if (importURL instanceof HttpURLConnection) {
HttpURLConnection httpURL = (HttpURLConnection) importURL;
String httpCL = httpURL.getHeaderField("Content-Location");
if (httpCL != null) {
importedURL = HTTPURL.getURL(importedURL, httpCL);
}
String mtype = httpURL.getContentType();
if (mtype == null) {
throw new FileNotFoundException(importURL.getURL() +
"No Media Type defined");
} else {
if (mtype.toLowerCase().indexOf("text/html") != -1) {
throw new FileNotFoundException(importURL.getURL() +
": You can't import" +
" an HTML document");
}
}
}
Frame f = ac.getFrame();
try {
CssFouffa cssFouffa = new CssFouffa(ac,
HTTPURL.getInputStream(ac, importURL),
importedURL, listeners, newVisited,
properties, mode);
cssFouffa.setOrigin(getOrigin());
if (!media.isEmpty()) {
cssFouffa.setAtRule(media);
} else {
cssFouffa.setAtRule(getAtRule());
}
cssFouffa.parseStyle();
} finally {
ac.setFrame(f);
}
} catch (Exception e) {
if (!Util.noErrorTrace) {
ac.getFrame()
.addError(new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(),
e));
}
}
}
/**
* Call by the at-rule statement.
*
* @param ident The ident for this at-rule (for example: 'font-face')
* @param string The string representation of this at-rule
*/
public void handleAtRule(String ident, String string) {
if (mode) {
for (CssValidatorListener listener : listeners) {
listener.handleAtRule(ac, ident, string);
}
} else {
if (!Util.noErrorTrace) {
// only @import ; or @import ; are valids in CSS1
ParseException error = new ParseException("at-rules are not implemented in CSS1");
ac.getFrame()
.addError(new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(),
error));
}
}
}
/**
* Treat the "\9" CSS declaration hack as a vendor extension warning
* rather than a fatal error?
*/
private boolean allowBackslash9Hack() {
return this.ac.getTreatCssHacksAsWarnings();
}
/**
* Assign an expression to a property. This function create a new property
* with property
and assign to it the expression with the
* importance.
*
* @param property the name of the property
* @param expression The expression representation of expression
* @param important true if expression id important
* @return a CssProperty
* @throw InvalidParamException
* An error appears during the property creation.
*/
public CssProperty handleDeclaration(String property, CssExpression expression, boolean important)
throws InvalidParamException {
CssProperty prop;
if (Util.onDebug) {
System.err.println("Creating " + property + ": " + expression);
}
final CssValue lastValue = expression.getLastValue();
if (allowBackslash9Hack() && lastValue != null && lastValue.hasBackslash9Hack()) {
expression.markCssHack();
}
try {
prop = properties.createProperty(ac, getAtRule(), property, expression);
} catch (InvalidParamException e) {
throw e;
} catch (Exception e) {
if (Util.onDebug) {
e.printStackTrace();
}
throw new InvalidParamException(e.toString(), ac);
}
// set the importance
if (important) {
prop.setImportant();
}
prop.setOrigin(origin);
// set informations for errors and warnings
prop.setInfo(ac.getFrame().getLine(), ac.getFrame().getSourceFile());
// ok, return the new property
return prop;
}
/**
* Assign an expression to a MediaFeature. This function create a new
* media feature with feature
and assign to it the expression
*
* @param feature the name of the media feature
* @param expression The expression representation of expression
* @return a CssProperty
* @throw InvalidParamException
* An error appears during the property creation.
*/
public MediaFeature handleMediaFeature(AtRuleMedia rule, String feature,
CssExpression expression)
throws InvalidParamException {
MediaFeature mf;
if (Util.onDebug) {
System.err.println("Creating MediaFeature" + feature + ": " + expression);
}
try {
mf = properties.createMediaFeature(ac, rule, feature, expression);
} catch (WarningParamException w) {
ac.getFrame().addWarning(w.getMessage(), feature);
return null;
} catch (InvalidParamException e) {
ac.getFrame().addError(new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(), e));
return null;
} catch (Exception e) {
e.printStackTrace();
if (Util.onDebug) {
e.printStackTrace();
}
throw new InvalidParamException(e.toString(), ac);
}
mf.setOrigin(origin);
// set informations for errors and warnings
mf.setInfo(ac.getFrame().getLine(), ac.getFrame().getSourceFile());
// ok, return the new property
return mf;
}
/**
* Parse only a list of declarations. This is useful to parse the
* STYLE
attribute in a HTML document.
*
*
* Example:
*
* CssFouffa parser =
* new CssFouffa(new URL("http://www.w3.org/drafts.css"));
* CssValidatorListener myListener = new MyParserListener();
* CssSelector selector = new CssSelector();
* selector.setElement("H1");
*
* parser.addListener(myListener);
* parser.parseDeclarations(selector);
*
*
* @param context The current context
* @see org.w3c.css.parser.CssFouffa#addListener
*/
public void parseDeclarations(CssSelectors context) {
// here we have an example for reusing the parser.
try {
ArrayList properties = attributeDeclarations();
if (properties != null && properties.size() != 0) {
handleRule(context, properties);
}
} catch (ParseException e) {
if (!Util.noErrorTrace) {
CssParseException ex = new CssParseException(e);
ex.skippedString = "";
ex.property = currentProperty;
ex.contexts = currentContext;
CssError error = new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(), ex);
ac.getFrame().addError(error);
}
}
if (!Util.noErrorTrace) {
Errors errors = ac.getFrame().getErrors();
Warnings warnings = ac.getFrame().getWarnings();
for (CssValidatorListener listener : listeners) {
listener.notifyErrors(errors);
listener.notifyWarnings(warnings);
}
}
}
/**
* used for the output of the stylesheet
*
* @param atRule the
* @rule that just has been found by the parser in the stylesheet, it is
* added to the storage for the output
*/
public void newAtRule(AtRule atRule) {
for (CssValidatorListener listener : listeners) {
listener.newAtRule(atRule);
}
}
/**
* used for the output of the stylesheet
*
* @param charset the
* @charset rule that has been found by the parser
*/
public void addCharSet(String charset) {
for (CssValidatorListener listener : listeners) {
listener.addCharSet(charset);
}
Charset c = null;
try {
c = Charset.forName(charset);
} catch (Exception ex) {
return;
}
Charset originalCharset = ac.getCharsetObjForURL(getURL());
if (originalCharset == null) {
ac.setCharsetForURL(getURL(), c);
try {
ReInit(ac, getURL());
} catch (IOException ioex) {
}
} else if (c.compareTo(originalCharset) != 0) {
InvalidParamException ex = new InvalidParamException("conflicting-charset",
new String[]{originalCharset.toString(), charset}, ac);
CssError cerr = new CssError(getSourceFile(), getBeginLine(),
getBeginColumn(), getEndLine(), getEndColumn(), ex);
ac.getFrame().addError(cerr);
}
}
/**
* used for the output of the stylesheet the
*
* @rule that had been found before is closed here after the content that's
* in it.
*/
public void endOfAtRule() {
for (CssValidatorListener listener : listeners) {
listener.endOfAtRule();
}
}
/**
* used for the output of the stylesheet
*
* @param important true if the rule was declared important in the stylesheet
*/
public void setImportant(boolean important) {
for (CssValidatorListener listener : listeners) {
listener.setImportant(important);
}
}
/**
* used for the output of the stylesheet
*
* @param selectors a list of one or more selectors to be added to the output
* stylesheet
*/
public void setSelectorList(ArrayList selectors) {
for (CssValidatorListener listener : listeners) {
listener.setSelectorList(selectors);
}
}
/**
* used for the output of the stylesheet
*
* @param properties A list of properties that are following eachother in the
* stylesheet (for example: all properties in an
* @rule)
*/
public void addProperty(ArrayList properties) {
for (CssValidatorListener listener : listeners) {
listener.setProperty(properties);
}
}
/**
* used for the output of the stylesheet used to close a rule when it has
* been read by the parser
*/
public void endOfRule() {
for (CssValidatorListener listener : listeners) {
listener.endOfRule();
}
}
/**
* used for the output of the stylesheet if an error is found this function
* is used to remove the whole stylerule from the memorystructure so that it
* won't appear on the screen
*/
public void removeThisRule() {
for (CssValidatorListener listener : listeners) {
listener.removeThisRule();
}
}
/**
* used for the output of the stylesheet if an error is found this function
* is used to remove the whole
*
* @rule from the memorystructure so that it won't appear on the screen
*/
public void removeThisAtRule() {
for (CssValidatorListener listener : listeners) {
listener.removeThisAtRule();
}
}
/**
* Adds a vector of properties to a selector.
*
* @param selector the selector
* @param declarations Properties to associate with contexts
*/
public void handleRule(CssSelectors selector, ArrayList declarations) {
for (CssValidatorListener listener : listeners) {
listener.handleRule(ac, selector, declarations);
}
}
/**
* Return the class name for a property
*
* @param property the property name ('font-size' for example)
* @return the class name ('org.w3c.css.properties.CssFontSize' for example)
*/
public String getProperty(String property) {
return properties.getProperty(property);
}
/**
* Set the style
*/
public void setStyle(Class style) {
ac.setCssSelectorsStyle(style);
}
public CssFouffa(java.io.InputStream stream) {
super(stream);
properties = new CssPropertyFactory("css21");
// loadConfig("css2", null);
}
public CssFouffa(java.io.Reader stream) {
super(stream);
properties = new CssPropertyFactory("css21");
// loadConfig("css2", null);
}
public CssFouffa(CssParserTokenManager tm) {
super(tm);
properties = new CssPropertyFactory("css21");
// loadConfig("css2", null);
}
public CssFouffa(ApplContext ac, Reader reader) {
super(reader);
this.ac = ac;
properties = new CssPropertyFactory(ac.getPropertyKey());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy