aboutsummaryrefslogtreecommitdiffstats
path: root/src/java/org/apache/fop/apps/FOURIResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/java/org/apache/fop/apps/FOURIResolver.java')
-rw-r--r--src/java/org/apache/fop/apps/FOURIResolver.java358
1 files changed, 203 insertions, 155 deletions
diff --git a/src/java/org/apache/fop/apps/FOURIResolver.java b/src/java/org/apache/fop/apps/FOURIResolver.java
index 13baeaa56..d68905e4c 100644
--- a/src/java/org/apache/fop/apps/FOURIResolver.java
+++ b/src/java/org/apache/fop/apps/FOURIResolver.java
@@ -19,7 +19,6 @@
package org.apache.fop.apps;
-import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -30,28 +29,34 @@ import java.net.URLConnection;
import javax.xml.transform.Source;
import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamSource;
// commons logging
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.fop.util.DataURIResolver;
-// base64 support for "data" urls
-import org.apache.xmlgraphics.util.io.Base64DecodeStream;
import org.apache.xmlgraphics.util.io.Base64EncodeStream;
/**
- * Provides FOP specific URI resolution.
- * This is the default URIResolver {@link FOUserAgent} will use unless overidden.
+ * Provides FOP specific URI resolution. This is the default URIResolver
+ * {@link FOUserAgent} will use unless overidden.
+ *
* @see javax.xml.transform.URIResolver
*/
-public class FOURIResolver
- implements javax.xml.transform.URIResolver {
-
+public class FOURIResolver implements javax.xml.transform.URIResolver {
+
// log
private Log log = LogFactory.getLog("FOP");
- // true if exceptions are to be thrown if the URIs cannot be resolved.
+ /** URIResolver for RFC 2397 data URLs */
+ private URIResolver dataURIResolver = new DataURIResolver();
+
+ /** A user settable URI Resolver */
+ private URIResolver uriResolver = null;
+
+ /** true if exceptions are to be thrown if the URIs cannot be resolved. */
private boolean throwExceptions = false;
/**
@@ -60,23 +65,28 @@ public class FOURIResolver
public FOURIResolver() {
this(false);
}
-
+
/**
* Additional constructor
- * @param throwExceptions true if exceptions are to be thrown if the URIs cannot be
- * resolved.
+ *
+ * @param throwExceptions
+ * true if exceptions are to be thrown if the URIs cannot be
+ * resolved.
*/
public FOURIResolver(boolean throwExceptions) {
this.throwExceptions = throwExceptions;
}
-
+
/**
* Handles resolve exceptions appropriately.
- * @param errorStr error string
- * @param strict strict user config
+ *
+ * @param errorStr
+ * error string
+ * @param strict
+ * strict user config
*/
private void handleException(Exception e, String errorStr, boolean strict)
- throws TransformerException {
+ throws TransformerException {
if (strict) {
throw new TransformerException(errorStr, e);
}
@@ -84,182 +94,220 @@ public class FOURIResolver
}
/**
- * Called by the processor through {@link FOUserAgent} when it encounters an
- * uri in an external-graphic element.
- * (see also {@link javax.xml.transform.URIResolver#resolve(String, String)}
- * This resolver will allow URLs without a scheme, i.e. it assumes 'file:' as
- * the default scheme. It also allows relative URLs with scheme,
- * e.g. file:../../abc.jpg which is not strictly RFC compliant as long as the
- * scheme is the same as the scheme of the base URL. If the base URL is null
+ * Called by the processor through {@link FOUserAgent} when it encounters an
+ * uri in an external-graphic element. (see also
+ * {@link javax.xml.transform.URIResolver#resolve(String, String)} This
+ * resolver will allow URLs without a scheme, i.e. it assumes 'file:' as the
+ * default scheme. It also allows relative URLs with scheme, e.g.
+ * file:../../abc.jpg which is not strictly RFC compliant as long as the
+ * scheme is the same as the scheme of the base URL. If the base URL is null
* a 'file:' URL referencing the current directory is used as the base URL.
- * If the method is successful it will return a Source of type
- * {@link javax.xml.transform.stream.StreamSource} with its SystemID set to
+ * If the method is successful it will return a Source of type
+ * {@link javax.xml.transform.stream.StreamSource} with its SystemID set to
* the resolved URL used to open the underlying InputStream.
*
- * @param href An href attribute, which may be relative or absolute.
- * @param base The base URI against which the first argument will be made
- * absolute if the absolute URI is required.
- * @return A {@link javax.xml.transform.Source} object, or null if the href
- * cannot be resolved.
- * @throws javax.xml.transform.TransformerException Never thrown by this implementation.
+ * @param href
+ * An href attribute, which may be relative or absolute.
+ * @param base
+ * The base URI against which the first argument will be made
+ * absolute if the absolute URI is required.
+ * @return A {@link javax.xml.transform.Source} object, or null if the href
+ * cannot be resolved.
+ * @throws javax.xml.transform.TransformerException
+ * Never thrown by this implementation.
* @see javax.xml.transform.URIResolver#resolve(String, String)
*/
- public Source resolve(String href, String base) throws TransformerException {
- // data URLs can be quite long so don't try to build a File (can lead to problems)
- if (href.startsWith("data:")) {
- return parseDataURI(href);
+ public Source resolve(String href, String base) throws TransformerException {
+ Source source = null;
+
+ // data URLs can be quite long so evaluate early and don't try to build a File
+ // (can lead to problems)
+ source = dataURIResolver.resolve(href, base);
+
+ // Custom uri resolution
+ if (source == null && uriResolver != null) {
+ source = uriResolver.resolve(href, base);
}
- URL absoluteURL = null;
- File file = new File(href);
- if (file.canRead() && file.isFile()) {
- try {
- absoluteURL = file.toURL();
- } catch (MalformedURLException mfue) {
- handleException(mfue,
- "Could not convert filename '" + href + "' to URL", throwExceptions);
- }
- } else {
- // no base provided
- if (base == null) {
- // We don't have a valid file protocol based URL
+ // Fallback to default resolution mechanism
+ if (source == null) {
+ URL absoluteURL = null;
+ File file = new File(href);
+ if (file.canRead() && file.isFile()) {
try {
- absoluteURL = new URL(href);
- } catch (MalformedURLException mue) {
+ absoluteURL = file.toURL();
+ } catch (MalformedURLException mfue) {
+ handleException(mfue, "Could not convert filename '" + href
+ + "' to URL", throwExceptions);
+ }
+ } else {
+ // no base provided
+ if (base == null) {
+ // We don't have a valid file protocol based URL
try {
- // the above failed, we give it another go in case
- // the href contains only a path then file: is assumed
- absoluteURL = new URL("file:" + href);
- } catch (MalformedURLException mfue) {
- handleException(mfue,
- "Error with URL '" + href + "'", throwExceptions);
+ absoluteURL = new URL(href);
+ } catch (MalformedURLException mue) {
+ try {
+ // the above failed, we give it another go in case
+ // the href contains only a path then file: is
+ // assumed
+ absoluteURL = new URL("file:" + href);
+ } catch (MalformedURLException mfue) {
+ handleException(mfue, "Error with URL '" + href
+ + "'", throwExceptions);
+ }
}
- }
- // try and resolve from context of base
- } else {
- URL baseURL = null;
- try {
- baseURL = new URL(base);
- } catch (MalformedURLException mfue) {
- handleException(mfue, "Error with base URL '" + base + "'", throwExceptions);
- }
+ // try and resolve from context of base
+ } else {
+ URL baseURL = null;
+ try {
+ baseURL = new URL(base);
+ } catch (MalformedURLException mfue) {
+ handleException(mfue, "Error with base URL '" + base
+ + "'", throwExceptions);
+ }
- /*
- * This piece of code is based on the following statement in
- * RFC2396 section 5.2:
- *
- * 3) If the scheme component is defined, indicating that the
- * reference starts with a scheme name, then the reference is
- * interpreted as an absolute URI and we are done. Otherwise,
- * the reference URI's scheme is inherited from the base URI's
- * scheme component.
- *
- * Due to a loophole in prior specifications [RFC1630], some
- * parsers allow the scheme name to be present in a relative URI
- * if it is the same as the base URI scheme. Unfortunately, this
- * can conflict with the correct parsing of non-hierarchical
- * URI. For backwards compatibility, an implementation may work
- * around such references by removing the scheme if it matches
- * that of the base URI and the scheme is known to always use
- * the <hier_part> syntax.
- *
- * The URL class does not implement this work around, so we do.
- */
- String scheme = baseURL.getProtocol() + ":";
- if (href.startsWith(scheme)) {
- href = href.substring(scheme.length());
- if ("file:".equals(scheme)) {
- int colonPos = href.indexOf(':');
- int slashPos = href.indexOf('/');
- if (slashPos >= 0 && colonPos >= 0 && colonPos < slashPos) {
- href = "/" + href; // Absolute file URL doesn't
- // have a leading slash
+ /*
+ * This piece of code is based on the following statement in
+ * RFC2396 section 5.2:
+ *
+ * 3) If the scheme component is defined, indicating that
+ * the reference starts with a scheme name, then the
+ * reference is interpreted as an absolute URI and we are
+ * done. Otherwise, the reference URI's scheme is inherited
+ * from the base URI's scheme component.
+ *
+ * Due to a loophole in prior specifications [RFC1630], some
+ * parsers allow the scheme name to be present in a relative
+ * URI if it is the same as the base URI scheme.
+ * Unfortunately, this can conflict with the correct parsing
+ * of non-hierarchical URI. For backwards compatibility, an
+ * implementation may work around such references by
+ * removing the scheme if it matches that of the base URI
+ * and the scheme is known to always use the <hier_part>
+ * syntax.
+ *
+ * The URL class does not implement this work around, so we
+ * do.
+ */
+ String scheme = baseURL.getProtocol() + ":";
+ if (href.startsWith(scheme)) {
+ href = href.substring(scheme.length());
+ if ("file:".equals(scheme)) {
+ int colonPos = href.indexOf(':');
+ int slashPos = href.indexOf('/');
+ if (slashPos >= 0 && colonPos >= 0
+ && colonPos < slashPos) {
+ href = "/" + href; // Absolute file URL doesn't
+ // have a leading slash
+ }
}
}
+ try {
+ absoluteURL = new URL(baseURL, href);
+ } catch (MalformedURLException mfue) {
+ handleException(mfue, "Error with URL; base '" + base
+ + "' " + "href '" + href + "'", throwExceptions);
+ }
}
+ }
+
+ if (absoluteURL != null) {
+ String effURL = absoluteURL.toExternalForm();
try {
- absoluteURL = new URL(baseURL, href);
- } catch (MalformedURLException mfue) {
- handleException(mfue,
- "Error with URL; base '" + base + "' " + "href '" + href + "'",
- throwExceptions);
+ URLConnection connection = absoluteURL.openConnection();
+ connection.setAllowUserInteraction(false);
+ connection.setDoInput(true);
+ updateURLConnection(connection, href);
+ connection.connect();
+ return new StreamSource(connection.getInputStream(), effURL);
+ } catch (FileNotFoundException fnfe) {
+ // Note: This is on "debug" level since the caller is
+ // supposed to handle this
+ log.debug("File not found: " + effURL);
+ } catch (java.io.IOException ioe) {
+ log.error("Error with opening URL '" + effURL + "': "
+ + ioe.getMessage());
}
}
}
-
- if (absoluteURL != null) {
- String effURL = absoluteURL.toExternalForm();
- try {
- URLConnection connection = absoluteURL.openConnection();
- connection.setAllowUserInteraction(false);
- connection.setDoInput(true);
- updateURLConnection(connection, href);
- connection.connect();
- return new StreamSource(connection.getInputStream(), effURL);
- } catch (FileNotFoundException fnfe) {
- //Note: This is on "debug" level since the caller is supposed to handle this
- log.debug("File not found: " + effURL);
- } catch (java.io.IOException ioe) {
- log.error("Error with opening URL '" + effURL + "': " + ioe.getMessage());
- }
- }
- return null;
+ return source;
}
/**
- * This method allows you to set special values on a URLConnection just before the connect()
- * method is called. Subclass FOURIResolver and override this method to do things like
- * adding the user name and password for HTTP basic authentication.
- * @param connection the URLConnection instance
- * @param href the original URI
+ * This method allows you to set special values on a URLConnection just
+ * before the connect() method is called. Subclass FOURIResolver and
+ * override this method to do things like adding the user name and password
+ * for HTTP basic authentication.
+ *
+ * @param connection
+ * the URLConnection instance
+ * @param href
+ * the original URI
*/
protected void updateURLConnection(URLConnection connection, String href) {
- //nop
+ // nop
}
-
+
/**
- * This is a convenience method for users who want to override updateURLConnection for
- * HTTP basic authentication. Simply call it using the right username and password.
- * @param connection the URLConnection to set up for HTTP basic authentication
- * @param username the username
- * @param password the password
+ * This is a convenience method for users who want to override
+ * updateURLConnection for HTTP basic authentication. Simply call it using
+ * the right username and password.
+ *
+ * @param connection
+ * the URLConnection to set up for HTTP basic authentication
+ * @param username
+ * the username
+ * @param password
+ * the password
*/
- protected void applyHttpBasicAuthentication(URLConnection connection,
+ protected void applyHttpBasicAuthentication(URLConnection connection,
String username, String password) {
String combined = username + ":" + password;
try {
- ByteArrayOutputStream baout = new ByteArrayOutputStream(combined.length() * 2);
+ ByteArrayOutputStream baout = new ByteArrayOutputStream(combined
+ .length() * 2);
Base64EncodeStream base64 = new Base64EncodeStream(baout);
- //TODO Not sure what charset/encoding can be used with basic authentication
+ // TODO Not sure what charset/encoding can be used with basic
+ // authentication
base64.write(combined.getBytes("UTF-8"));
base64.close();
- connection.setRequestProperty("Authorization",
- "Basic " + new String(baout.toByteArray(), "UTF-8"));
+ connection.setRequestProperty("Authorization", "Basic "
+ + new String(baout.toByteArray(), "UTF-8"));
} catch (IOException e) {
- //won't happen. We're operating in-memory.
- throw new RuntimeException("Error during base64 encodation of username/password");
+ // won't happen. We're operating in-memory.
+ throw new RuntimeException(
+ "Error during base64 encodation of username/password");
}
}
-
+
/**
- * Parses inline data URIs as generated by MS Word's XML export and FO stylesheet.
- * @see <a href="http://www.ietf.org/rfc/rfc2397">RFC 2397</a>
+ * Sets the custom URI Resolver. It is used for resolving factory-level URIs like
+ * hyphenation patterns and as backup for URI resolution performed during a
+ * rendering run.
+ *
+ * @param resolver
+ * the new URI resolver
*/
- private Source parseDataURI(String href) {
- int commaPos = href.indexOf(',');
- // header is of the form data:[<mediatype>][;base64]
- String header = href.substring(0, commaPos);
- String data = href.substring(commaPos + 1);
- if (header.endsWith(";base64")) {
- byte[] bytes = data.getBytes();
- ByteArrayInputStream encodedStream = new ByteArrayInputStream(bytes);
- Base64DecodeStream decodedStream = new Base64DecodeStream(encodedStream);
- return new StreamSource(decodedStream);
- } else {
- //Note that this is not quite the full story here. But since we are only interested
- //in base64-encoded binary data, the next line will probably never be called.
- return new StreamSource(new java.io.StringReader(data));
- }
+ public void setCustomURIResolver(URIResolver resolver) {
+ this.uriResolver = resolver;
+ }
+
+ /**
+ * Returns the custom URI Resolver.
+ *
+ * @return the URI Resolver or null, if none is set
+ */
+ public URIResolver getCustomURIResolver() {
+ return this.uriResolver;
+ }
+
+ /**
+ * @param throwExceptions
+ * Whether or not to throw exceptions on resolution error
+ */
+ public void setThrowExceptions(boolean throwExceptions) {
+ this.throwExceptions = throwExceptions;
}
}