/* * (c) COPYRIGHT 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$ */ package org.w3c.css.util; import org.w3c.css.css.StyleSheet; import org.w3c.css.parser.Frame; import org.w3c.www.http.HttpAcceptCharset; import org.w3c.www.http.HttpAcceptCharsetList; import org.w3c.www.http.HttpFactory; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.IllegalCharsetNameException; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.HashMap; /** * @author Philippe Le Hegaret * @version $Revision$ */ public class ApplContext { // the charset of the first source (url/uploaded/text) public static Charset defaultCharset; public static Charset utf8Charset; static { try { defaultCharset = Charset.forName("iso-8859-1"); utf8Charset = Charset.forName("utf-8"); } catch (Exception ex) { // we are in deep trouble here defaultCharset = null; utf8Charset = null; } } private class BomEncoding { Charset uriCharset = null; boolean fromBom = false; private BomEncoding(Charset c, boolean fb) { uriCharset = c; fromBom = fb; } private BomEncoding(Charset c) { this(c, false); } private BomEncoding() { } } // charset definition of traversed URLs private HashMap uricharsets = null; // namespace definitions private HashMap> namespaces = null; // default prefix public static String defaultPrefix = "*defaultprefix*"; public static String noPrefix = "*noprefix*"; private ArrayList linkedmedia = new ArrayList(); String credential = null; String lang; Messages msgs; Frame frame; StyleSheet styleSheet = null; CssVersion version = CssVersion.getDefault(); CssProfile profile = CssProfile.NONE; String input; Class cssselectorstyle; int origin = -1; String medium; private String link; int warningLevel = 0; boolean treatVendorExtensionsAsWarnings = false; boolean treatCssHacksAsWarnings = false; boolean suggestPropertyName = true; private String propertyKey = null; public boolean followlinks() { return followlinks; } public void setFollowlinks(boolean followlinks) { this.followlinks = followlinks; } boolean followlinks = true; FakeFile fakefile = null; String faketext = null; Charset faketextcharset = null; URL fakeurl = null; URL referrer = null; /** * Creates a new ApplContext */ public ApplContext(String lang) { this.lang = lang; msgs = new Messages(lang); } public int getWarningLevel() { return warningLevel; } public void setWarningLevel(int warningLevel) { this.warningLevel = warningLevel; } public ArrayList getLinkedURIs() { return linkedmedia; } public void addLinkedURI(URL url) { if (url != null) { linkedmedia.add(url); } } // as ugly as everything else public String getCredential() { return credential; } public void setCredential(String credential) { this.credential = credential; } public void setFrame(Frame frame) { this.frame = frame; frame.ac = this; } public Frame getFrame() { return frame; } public void setStyleSheet(StyleSheet styleSheet) { this.styleSheet = styleSheet; } public StyleSheet getStyleSheet() { return styleSheet; } public Class getCssSelectorsStyle() { return cssselectorstyle; } public void setCssSelectorsStyle(Class s) { cssselectorstyle = s; } public Messages getMsg() { return msgs; } public String getContentType() { return (msgs != null) ? msgs.getString("content-type") : null; } public String getContentLanguage() { return (msgs != null) ? msgs.getString("content-language") : null; } /** * Searches the properties list for a content-encoding one. If it does not * exist, searches for output-encoding-name. If it still does not exists, * the method returns the default utf-8 value * * @return the output encoding of this ApplContext */ public String getContentEncoding() { // return (msgs != null) ? msgs.getString("content-encoding") : null; String res = null; if (msgs != null) { res = msgs.getString("content-encoding"); if (res == null) { res = msgs.getString("output-encoding-name"); } if (res != null) { // if an encoding has been found, return it return res; } } // default encoding return Utf8Properties.ENCODING; } public String getLang() { return lang; } public void setCssVersion(String cssversion) { version = CssVersion.resolve(this, cssversion); propertyKey = null; } public void setCssVersion(CssVersion version) { this.version = version; propertyKey = null; } public String getCssVersionString() { return version.toString(); } public CssVersion getCssVersion() { return version; } public void setProfile(String profile) { this.profile = CssProfile.resolve(this, profile); propertyKey = null; } /** * get the String used to fetch the relevant property file */ public String getPropertyKey() { if (propertyKey != null) { return propertyKey; } if (profile == CssProfile.SVG && version == CssVersion.CSS3) { propertyKey = version.toString() + profile.toString(); return propertyKey; } if (profile != CssProfile.EMPTY && profile != CssProfile.NONE) { propertyKey = profile.toString(); } else { propertyKey = version.toString(); } return propertyKey; } public CssProfile getCssProfile() { return profile; } public String getProfileString() { return profile.toString(); } public void setCssVersionAndProfile(String spec) { // for things like SVG, version will be set to the default one // CSS21 in that case, as defined in CssVersion // and profile will be resolved to svg. // // if the version resolve then profile will default to NONE // TODO should we check profile first and if SVG or MOBILE // set specific version of CSS (like CSS2 and not CSS21 for MOBILE) ? if ((spec == null) || spec.isEmpty()) { version = CssVersion.getDefault(); profile = CssProfile.SVG; } else { String low = spec.toLowerCase(); version = CssVersion.resolve(this, low); profile = CssProfile.resolve(this, low); // some special cases... // http://www.atsc.org/cms/index.php/standards/published-standards/71-atsc-a100-standard if (profile.equals(CssProfile.ATSCTV)) { version = CssVersion.CSS2; } } } public void setOrigin(int origin) { this.origin = origin; } public int getOrigin() { return origin; } public void setMedium(String medium) { this.medium = medium; } public String getMedium() { return medium; } public String getInput() { return input; } public void setInput(String input) { this.input = input; } public String getLink() { return link; } public void setLink(String queryString) { this.link = queryString; } public boolean getTreatVendorExtensionsAsWarnings() { return treatVendorExtensionsAsWarnings; } /** * Change the behaviour of error reporting for vendor extensions. * * @param treatVendorExtensionsAsWarnings * */ public void setTreatVendorExtensionsAsWarnings( boolean treatVendorExtensionsAsWarnings) { this.treatVendorExtensionsAsWarnings = treatVendorExtensionsAsWarnings; } public boolean getTreatCssHacksAsWarnings() { return treatCssHacksAsWarnings; } /** * Change the behaviour of error reporting for CSS Hacks. * * @param treatCssHacksAsWarnings */ public void setTreatCssHacksAsWarnings(boolean treatCssHacksAsWarnings) { this.treatCssHacksAsWarnings = treatCssHacksAsWarnings; } public boolean getSuggestPropertyName() { return suggestPropertyName; } public void setSuggestPropertyName(boolean b) { suggestPropertyName = b; } /** * Sets the content encoding to the first charset that appears in * acceptCharset. If the charset is not supported, the content * encoding will be utf-8 * * @param acceptCharset a String representing the Accept-Charset request parameter */ public void setContentEncoding(String acceptCharset) { if (acceptCharset != null) { // uses some Jigsaw classes to parse the Accept-Charset // these classes need to load a lot of stuff, so it may be quite // long the first time HttpAcceptCharsetList charsetList; HttpAcceptCharset[] charsets; charsetList = HttpFactory.parseAcceptCharsetList(acceptCharset); charsets = (HttpAcceptCharset[]) charsetList.getValue(); String encoding = null; double quality = 0.0; String biasedcharset = getMsg().getString("output-encoding-name"); for (int i = 0; i < charsets.length && quality < 1.0; i++) { HttpAcceptCharset charset = charsets[i]; String currentCharset = charset.getCharset(); // checks that the charset is supported by Java if (isCharsetSupported(currentCharset)) { double currentQuality = charset.getQuality(); // we prefer utf-8 // FIXME (the bias value and the biased charset // should be dependant on the language) if ((biasedcharset != null) && !biasedcharset.equalsIgnoreCase(currentCharset)) { currentQuality *= 0.5; } if (currentQuality > quality) { quality = currentQuality; encoding = charset.getCharset(); } } } if (encoding != null) { getMsg().properties.setProperty("content-encoding", encoding); } else { // no valid charset getMsg().properties.remove("content-encoding"); } } else { // no Accept-Charset given getMsg().properties.remove("content-encoding"); } } private boolean isCharsetSupported(String charset) { if ("*".equals(charset)) { return true; } try { return Charset.isSupported(charset); } catch (Exception e) { return false; } } /** * used for storing the charset of the document in use * and its update by a @charset statement, or through * automatic discovery */ public void setCharsetForURL(URL url, String charset, boolean from_bom) { if (uricharsets == null) { uricharsets = new HashMap<>(); } Charset c = null; try { c = Charset.forName(charset); } catch (IllegalCharsetNameException icex) { // FIXME add a warning in the CSS } catch (UnsupportedCharsetException ucex) { // FIXME inform about lack of support } if (c != null) { uricharsets.put(url, new BomEncoding(c, from_bom)); } } /** * used for storing the charset of the document in use * and its update by a @charset statement, or through * automatic discovery */ public void setCharsetForURL(URL url, Charset charset) { if (uricharsets == null) { uricharsets = new HashMap(); } uricharsets.put(url, new BomEncoding(charset)); } public boolean isCharsetFromBOM(URL url) { BomEncoding b; if (uricharsets == null) { return false; } b = uricharsets.get(url); if (b != null) { return b.fromBom; } return false; } /** * used for storing the charset of the document in use * and its update by a @charset statement, or through * automatic discovery */ public String getCharsetForURL(URL url) { BomEncoding b; if (uricharsets == null) { return null; } b = uricharsets.get(url); if (b != null) { return b.uriCharset.toString(); } return null; } /** * used for storing the charset of the document in use * and its update by a @charset statement, or through * automatic discovery */ public Charset getCharsetObjForURL(URL url) { BomEncoding b; if (uricharsets == null) { return null; } b = uricharsets.get(url); if (b == null) { return null; } return b.uriCharset; } /** * store content of uploaded file */ public void setFakeFile(FakeFile fakefile) { this.fakefile = fakefile; } /** * store content of entered text */ public void setFakeText(String faketext, Charset faketextcharset) { this.faketext = faketext; this.faketextcharset = faketextcharset; } public InputStream getFakeInputStream(URL source) throws IOException { InputStream is = null; Charset c = null; if (fakefile != null) { is = fakefile.getInputStream(); } if (faketext != null) { is = new ByteArrayInputStream(faketext.getBytes(faketextcharset)); c = faketextcharset; } if (is == null) { return null; } if (c == null) { c = getCharsetObjForURL(source); } if (c == null) { UnicodeInputStream uis = new UnicodeInputStream(is); String guessedCharset = uis.getEncodingFromStream(); if (guessedCharset != null) { setCharsetForURL(source, guessedCharset, true); } return uis; } else { if (utf8Charset.compareTo(c) == 0) { return new UnicodeInputStream(is); } } return is; } public boolean isInputFake() { return ((faketext != null) || (fakefile != null)); } public void setFakeURL(String fakeurl) { try { this.fakeurl = new URL(fakeurl); } catch (Exception ex) { } } public URL getFakeURL() { return fakeurl; } /** * support for namespaces */ public void setNamespace(URL url, String prefix, String nsname) { if (namespaces == null) { namespaces = new HashMap>(); } // reformat the prefix if null. String realPrefix = ((prefix != null) && !prefix.isEmpty()) ? prefix : defaultPrefix; HashMap nsdefs = namespaces.get(url); if (nsdefs == null) { nsdefs = new HashMap(); nsdefs.put(realPrefix, nsname); namespaces.put(url, nsdefs); } else { // do we need to check if we have a redefinition ? nsdefs.put(realPrefix, nsname); } } // true if a namespace is defined in the document (CSS fragment) // defined by the URL, with prefix "prefix" public boolean isNamespaceDefined(URL url, String prefix) { if (prefix == null) { // no prefix, always match return true; } if (prefix.equals("*")) { // any ns, always true return true; } String realPrefix = (!prefix.isEmpty()) ? prefix : defaultPrefix; if (namespaces == null) { // no ns defined -> fail return false; } HashMap nsdefs = namespaces.get(url); if (nsdefs == null) { return false; } return nsdefs.containsKey(realPrefix); } /** * Set the current referrer for possible linked style sheets * * @param referrer the referring URL */ public void setReferrer(URL referrer) { this.referrer = referrer; } /** * get the referrer URL (or null if not relevant) * * @return an URL */ public URL getReferrer() { return referrer; } }