//// $Id$// From Philippe Le Hegaret (Philippe.Le_Hegaret@sophia.inria.fr)//// (c) COPYRIGHT MIT and INRIA, 1997.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage org.w3c.css.css;import org.w3c.css.atrules.css.AtRuleMedia;import org.w3c.css.atrules.css.AtRulePage;import org.w3c.css.parser.AtRule;import org.w3c.css.parser.CssError;import org.w3c.css.parser.CssFouffa;import org.w3c.css.parser.CssParseException;import org.w3c.css.parser.CssSelectors;import org.w3c.css.parser.CssValidatorListener;import org.w3c.css.parser.Errors;import org.w3c.css.parser.analyzer.ParseException;import org.w3c.css.parser.analyzer.TokenMgrError;import org.w3c.css.properties.css.CssProperty;import org.w3c.css.selectors.IdSelector;import org.w3c.css.util.ApplContext;import org.w3c.css.util.CssVersion;import org.w3c.css.util.InvalidParamException;import org.w3c.css.util.Messages;import org.w3c.css.util.Util;import org.w3c.css.util.Warning;import org.w3c.css.util.Warnings;import java.io.ByteArrayInputStream;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.io.Reader;import java.io.StringReader;import java.io.UnsupportedEncodingException;import java.lang.reflect.Constructor;import java.net.URL;import java.util.ArrayList;import java.util.StringTokenizer;/** * @version $Revision$ */public final class StyleSheetParser        implements CssValidatorListener, CssParser {    private static Constructor co = null;    static {        try {            Class c = java.lang.Exception.class;            Class cp[] = {java.lang.Exception.class};            co = c.getDeclaredConstructor(cp);        } catch (NoSuchMethodException ex) {            co = null;        }    }    CssFouffa cssFouffa;    StyleSheet style = new StyleSheet();    public void reInit() {        style = new StyleSheet();    }    public StyleSheet getStyleSheet() {        return style;    }    public void setWarningLevel(int warningLevel) {        style.setWarningLevel(warningLevel);    }    public void notifyErrors(Errors errors) {        style.addErrors(errors);    }    public void notifyWarnings(Warnings warnings) {        style.addWarnings(warnings);    }    /**     * Adds a vector of properties to a selector.     *     * @param selector   the selector     * @param properties Properties to associate with contexts     */    public void handleRule(ApplContext ac, CssSelectors selector,                           ArrayList<CssProperty> properties) {        if (selector.getAtRule() instanceof AtRulePage) {            style.remove(selector);        }        for (CssProperty property : properties) {            property.setSelectors(selector);            style.addProperty(selector, property);        }    }    // part added by Sijtsche de Jong    public void addCharSet(String charset) {        style.addCharSet(charset);    }    public void newAtRule(AtRule atRule) {        style.newAtRule(atRule);    }    public void endOfAtRule() {        style.endOfAtRule();    }    public void setImportant(boolean important) {        style.setImportant(important);    }    public void setSelectorList(ArrayList<CssSelectors> selectors) {        style.setSelectorList(selectors);    }    public void setProperty(ArrayList<CssProperty> properties) {        style.setProperty(properties);    }    public void endOfRule() {        style.endOfRule();    }    public void removeThisRule() {        style.removeThisRule();    }    public void removeThisAtRule() {        style.removeThisAtRule();    }    //end of part added by Sijtsche de Jong    /**     * Handles an at-rule.     * <p/>     * <p>The parameter <code>value</code> can be :     * <DL>     * <DT>CssString     * <DD>The value coming from a string.     * <DT>CssURL     * <DD>The value coming from an URL.     * <DT>Vector     * <DD>The value is a vector of declarations (it contains properties).     * This feature is not legal, so be careful.     * </DL>     *     * @param ident  The ident for this at-rule (for example: 'font-face')     * @param string The string representation if this at-rule     */    public void handleAtRule(ApplContext ac, String ident, String string) {        style.getWarnings().addWarning(new Warning(cssFouffa.getSourceFile(),                cssFouffa.getLine(),                "at-rule",                2,                new String[]{ident, string},                ac));        //stylesheet.addAtRule(atRule);    }    /**     * @param url    the URL containing the style sheet     * @param title  the title of the stylesheet     * @param kind   may be a stylesheet or an alternate stylesheet     * @param media  the media to apply this     * @param origin the origin of the style sheet     * @throws IOException an IO error     */    public void parseURL(ApplContext ac, URL url, String title,                         String kind, String media,                         int origin) {        boolean doneref = false;        URL ref = ac.getReferrer();        setWarningLevel(ac.getWarningLevel());        if (Util.onDebug) {            System.err.println("StyleSheet.parseURL(" + url + ", "                    + title + ", "                    + kind + ", " + media + ", "                    + origin + ")");        }        if (kind != null) {            kind = kind.trim().toLowerCase();            if (!kind.equals("stylesheet") && !kind.equals("alternate stylesheet")) {                return;            }        }        try {            ac.setOrigin(origin);//	    if (cssFouffa == null) {            cssFouffa = new CssFouffa(ac, url);            cssFouffa.addListener(this);//	    } else {//		cssFouffa.ReInit(ac, url);//	    }            //	    cssFouffa.setResponse(res);            // removed plh 2001-03-08            // cssFouffa.setOrigin(origin);            //	    cssFouffa.setDefaultMedium(defaultmedium);            //	    cssFouffa.doConfig();            if (media == null) {                if (ac.getCssVersion() != CssVersion.CSS1) {                    if (ac.getMedium() == null) {                        media = "all";                    } else {                        media = ac.getMedium();                    }                }            }            AtRuleMedia m = AtRuleMedia.getInstance(ac.getCssVersion());            try {                if (media != null) {                    addMedias(m, media, ac);                }                cssFouffa.setAtRule(m);            } catch (org.w3c.css.util.InvalidParamException e) {                Errors er = new Errors();                er.addError(new org.w3c.css.parser.CssError(url.toString(),                        -1, e));                notifyErrors(er);                return;            }            ac.setReferrer(url);            doneref = true;            cssFouffa.parseStyle();        } catch (Exception e) {            Errors er = new Errors();            er.addError(new org.w3c.css.parser.CssError(Messages.escapeString(url.toString()),                    // BUG: CWE-79 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')                    // -1, e));                    // FIXED:                    -1, new Exception(Messages.escapeString(e.getMessage()))));            notifyErrors(er);        } finally {            if (doneref) {                ac.setReferrer(ref);            }        }    }    // add media, easy version for CSS version < 3, otherwise, reuse the parser    private void addMedias(AtRuleMedia m, String medias, ApplContext ac) throws InvalidParamException {        // before CSS3, let's parse it the easy way...        if (ac.getCssVersion().compareTo(CssVersion.CSS3) < 0) {            StringTokenizer tokens = new StringTokenizer(medias, ",");            while (tokens.hasMoreTokens()) {                m.addMedia(null, tokens.nextToken().trim(), ac);            }        } else {            CssFouffa muP = new CssFouffa(ac, new StringReader(medias));            try {                AtRuleMedia arm = muP.parseMediaDeclaration();                if (arm != null) {                    m.allMedia = arm.allMedia;                }            } catch (ParseException pex) {                // error already added, so nothing else to do            }        }    }    /**     * Parse a style element. The Style element always comes from the user     *     * @param reader the reader containing the style data     * @param url    the name of the file the style element was read in.     * @throws IOException an IO error     */    public void parseStyleElement(ApplContext ac, Reader reader,                                  String title, String media,                                  URL url, int lineno) {        boolean doneref = false;        style.setWarningLevel(ac.getWarningLevel());        if (Util.onDebug) {            System.err.println("StyleSheet.parseStyleElement(" + title + ", "                    + media + ", " + url                    + "," + lineno + ")");        }        URL ref = ac.getReferrer();        try {//	    if (cssFouffa == null) {            String charset = ac.getCharsetForURL(url);            cssFouffa = new CssFouffa(ac, reader, url, lineno);            cssFouffa.addListener(this);//	    } else {//		cssFouffa.ReInit(ac, input, url, lineno);//	    }            //	    cssFouffa.setResponse(res);            //	    cssFouffa.setDefaultMedium(defaultmedium);            //	    cssFouffa.doConfig();            if (media == null && ac.getCssVersion() != CssVersion.CSS1) {                media = "all";            }            AtRuleMedia m = AtRuleMedia.getInstance(ac.getCssVersion());            try {                if (media != null) {                    addMedias(m, media, ac);                }                cssFouffa.setAtRule(m);            } catch (org.w3c.css.util.InvalidParamException e) {                Errors er = new Errors();                er.addError(new org.w3c.css.parser.CssError(url.toString(),                        -1, e));                notifyErrors(er);                return;            }            ac.setReferrer(url);            doneref = true;            cssFouffa.parseStyle();        } catch (IOException e) {            Errors er = new Errors();            er.addError(new org.w3c.css.parser.CssError(url.toString(),                    -1, e));            notifyErrors(er);        } catch (TokenMgrError e) {            Errors er = new Errors();            CssParseException cpe = null;            if (co != null) {                try {                    Object o[] = new Object[1];                    o[0] = e;                    Exception new_e = (Exception) co.newInstance(o);                    cpe = new CssParseException(new_e);                } catch (Exception ex) {                    cpe = null;                }            }            if (cpe == null) {                cpe = new CssParseException(new Exception(e.getMessage()));            }            er.addError(new org.w3c.css.parser.CssError(url.toString(),                    -1,                    //e.getErrorLine(),                    cpe));            notifyErrors(er);        } catch (RuntimeException e) {            Errors er = new Errors();            er.addError(new org.w3c.css.parser.CssError(url.toString(),                    cssFouffa.getLine(),                    new CssParseException(e)));            notifyErrors(er);        } finally {            if (doneref) {                ac.setReferrer(ref);            }        }    }    /**     * @param input the inputStream containing the style data     * @param url   the name of the file the style element was read in.     * @throws IOException an IO error     * @see #parseStyleElement(ApplContext, InputStream, String, String, URL, int)     * @deprecated Replaced by parseStyleElement     */    public void parseStyleElement(ApplContext ac, String input, URL url, int lineno) {        parseStyleElement(ac, new StringReader(input), null, null, url, lineno);    }    /**     * Parse a style element. The Style element always comes from the user     *     * @param input the input stream containing the style data     * @param url   the name of the file the style element was read in.     * @throws IOException an IO error     */    public void parseStyleElement(ApplContext ac, InputStream input,                                  String title, String media,                                  URL url, int lineno) {        InputStreamReader reader = null;        String charset = ac.getCharsetForURL(url);        try {            reader = new InputStreamReader(input, (charset == null) ?                    "iso-8859-1" : charset);        } catch (UnsupportedEncodingException uex) {            Errors er = new Errors();            er.addError(new org.w3c.css.parser.CssError(url.toString(),                    -1, uex));            notifyErrors(er);        } catch (Exception ex) {            // in case of error, ignore it.            reader = null;            if (Util.onDebug) {                System.err.println("Error in StyleSheet.parseStyleElement(" + title + ","                        + url + "," + lineno + ")");            }        }        if (reader != null) {            parseStyleElement(ac, reader, title, media, url, lineno);        }    }    /**     * Unify call to the parser for css doc as a reader.     *     * @param ac     * @param reader     * @param docref     */    public void parseStyleSheet(ApplContext ac, Reader reader, URL docref) {        parseStyleElement(ac, reader, null, null, (docref == null) ? ac.getFakeURL() : docref, 0);    }    /**     * Parse some declarations. All declarations always comes from the user     *     * @param input the inputStream containing the style data     * @param id    the uniq id     * @param url   the URL the style element was read in.     * @throws IOException an IO error     */    public void parseStyleAttribute(ApplContext ac, InputStream input, String id,                                    URL url, int lineno) {        style.setWarningLevel(ac.getWarningLevel());        lineno--; // why ?!?!        if (Util.onDebug) {            System.err.println("StyleSheet.parseStyleAttribute(" + id + ","                    + url + "," + lineno + ")");        }        try {            //	    if (cssFouffa == null) {            String charset = ac.getCharsetForURL(url);            cssFouffa = new CssFouffa(ac, input, charset, url, lineno);            cssFouffa.addListener(this);            //	    } else//		cssFouffa.ReInit(ac, input, url, lineno);            CssSelectors selector = new CssSelectors(ac);            try {                AtRuleMedia media = AtRuleMedia.getInstance(ac.getCssVersion());                if (ac.getCssVersion() != CssVersion.CSS1) {                    media.addMedia(null, "all", ac);                }                cssFouffa.setAtRule(media);            } catch (InvalidParamException e) {            } //ignore            try {                if (id == null || id.length() == 0) {                    id = "nullId-" + Long.toHexString(System.currentTimeMillis());                    // TODO add an error/warning ?                }                selector.addId(new IdSelector(id.substring(1)));            } catch (InvalidParamException e) {                style.removeThisRule();                ac.getFrame().addError(new CssError(e));            }            cssFouffa.parseDeclarations(selector);        } catch (IOException e) {            Errors er = new Errors();            er.addError(new org.w3c.css.parser.CssError(url.toString(),                    -1, e));            notifyErrors(er);        }    }    /**     * @param input the inputStream containing the style data     * @param id    the uniq id     * @param url   the name of the file the style element was read in.     * @throws IOException an IO error     * @see #parseStyleAttribute(ApplContext, InputStream, String, URL, int)     * @deprecated Replaced by parseStyleAttribute     */    public void parseStyleAttribute(ApplContext ac, String input, String id,                                    URL url, int lineno) {        parseStyleAttribute(ac, new ByteArrayInputStream(input.getBytes()),                id, url, lineno);    }    public void setStyle(Class style) {        cssFouffa.setStyle(style);    }}