KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > fop > apps > FOURIResolver


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 /* $Id: FOURIResolver.java 426576 2006-07-28 15:44:37Z jeremias $ */
19
20 package org.apache.fop.apps;
21
22 import java.io.ByteArrayInputStream JavaDoc;
23 import java.io.ByteArrayOutputStream JavaDoc;
24 import java.io.File JavaDoc;
25 import java.io.FileNotFoundException JavaDoc;
26 import java.io.IOException JavaDoc;
27 import java.net.MalformedURLException JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.net.URLConnection JavaDoc;
30
31 import javax.xml.transform.Source JavaDoc;
32 import javax.xml.transform.stream.StreamSource JavaDoc;
33
34 // commons logging
35
import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37
38 // base64 support for "data" urls
39
import org.apache.xmlgraphics.util.io.Base64DecodeStream;
40 import org.apache.xmlgraphics.util.io.Base64EncodeStream;
41
42 /**
43  * Provides FOP specific URI resolution.
44  * This is the default URIResolver {@link FOUserAgent} will use unless overidden.
45  * @see javax.xml.transform.URIResolver
46  */

47 public class FOURIResolver
48     implements javax.xml.transform.URIResolver JavaDoc {
49     
50     private Log log = LogFactory.getLog("FOP");
51     
52     /**
53      * Called by the processor through {@link FOUserAgent} when it encounters an
54      * uri in an external-graphic element.
55      * (see also {@link javax.xml.transform.URIResolver#resolve(String, String)}
56      * This resolver will allow URLs without a scheme, i.e. it assumes 'file:' as
57      * the default scheme. It also allows relative URLs with scheme,
58      * e.g. file:../../abc.jpg which is not strictly RFC compliant as long as the
59      * scheme is the same as the scheme of the base URL. If the base URL is null
60      * a 'file:' URL referencing the current directory is used as the base URL.
61      * If the method is successful it will return a Source of type
62      * {@link javax.xml.transform.stream.StreamSource} with its SystemID set to
63      * the resolved URL used to open the underlying InputStream.
64      *
65      * @param href An href attribute, which may be relative or absolute.
66      * @param base The base URI against which the first argument will be made
67      * absolute if the absolute URI is required.
68      * @return A {@link javax.xml.transform.Source} object, or null if the href
69      * cannot be resolved.
70      * @throws javax.xml.transform.TransformerException Never thrown by this implementation.
71      * @see javax.xml.transform.URIResolver#resolve(String, String)
72      */

73     public Source JavaDoc resolve(String JavaDoc href, String JavaDoc base)
74         throws javax.xml.transform.TransformerException JavaDoc {
75         
76         //data URLs can be quite long so don't try to build a File (can lead to problems)
77
if (href.startsWith("data:")) {
78             return parseDataURI(href);
79         }
80         
81         URL JavaDoc absoluteURL = null;
82         File JavaDoc f = new File JavaDoc(href);
83         if (f.exists()) {
84             try {
85                 absoluteURL = f.toURL();
86             } catch (MalformedURLException JavaDoc mfue) {
87                 log.error("Could not convert filename to URL: " + mfue.getMessage(), mfue);
88             }
89         } else {
90             URL JavaDoc baseURL = toBaseURL(base);
91             if (baseURL == null) {
92                 // We don't have a valid baseURL just use the URL as given
93
try {
94                     absoluteURL = new URL JavaDoc(href);
95                 } catch (MalformedURLException JavaDoc mue) {
96                     try {
97                         // the above failed, we give it another go in case
98
// the href contains only a path then file: is assumed
99
absoluteURL = new URL JavaDoc("file:" + href);
100                     } catch (MalformedURLException JavaDoc mfue) {
101                         log.error("Error with URL '" + href + "': " + mue.getMessage(), mue);
102                         return null;
103                     }
104                 }
105             } else {
106                 try {
107                     /*
108                         This piece of code is based on the following statement in
109                         RFC2396 section 5.2:
110
111                         3) If the scheme component is defined, indicating that the reference
112                            starts with a scheme name, then the reference is interpreted as an
113                            absolute URI and we are done. Otherwise, the reference URI's
114                            scheme is inherited from the base URI's scheme component.
115
116                            Due to a loophole in prior specifications [RFC1630], some parsers
117                            allow the scheme name to be present in a relative URI if it is the
118                            same as the base URI scheme. Unfortunately, this can conflict
119                            with the correct parsing of non-hierarchical URI. For backwards
120                            compatibility, an implementation may work around such references
121                            by removing the scheme if it matches that of the base URI and the
122                            scheme is known to always use the <hier_part> syntax.
123
124                         The URL class does not implement this work around, so we do.
125                     */

126
127                     String JavaDoc scheme = baseURL.getProtocol() + ":";
128                     if (href.startsWith(scheme)) {
129                         href = href.substring(scheme.length());
130                         if ("file:".equals(scheme)) {
131                             int colonPos = href.indexOf(':');
132                             int slashPos = href.indexOf('/');
133                             if (slashPos >= 0 && colonPos >= 0 && colonPos < slashPos) {
134                                 href = "/" + href; //Absolute file URL doesn't have a leading slash
135
}
136                         }
137                     }
138                     absoluteURL = new URL JavaDoc(baseURL, href);
139                 } catch (MalformedURLException JavaDoc mfue) {
140                     log.error("Error with URL '" + href + "': " + mfue.getMessage(), mfue);
141                     return null;
142                 }
143             }
144         }
145         
146         String JavaDoc effURL = absoluteURL.toExternalForm();
147         try {
148             URLConnection JavaDoc connection = absoluteURL.openConnection();
149             connection.setAllowUserInteraction(false);
150             connection.setDoInput(true);
151             updateURLConnection(connection, href);
152             connection.connect();
153             return new StreamSource JavaDoc(connection.getInputStream(), effURL);
154         } catch (FileNotFoundException JavaDoc fnfe) {
155             //Note: This is on "debug" level since the caller is supposed to handle this
156
log.debug("File not found: " + effURL);
157         } catch (java.io.IOException JavaDoc ioe) {
158             log.error("Error with opening URL '" + href + "': " + ioe.getMessage(), ioe);
159         }
160         return null;
161     }
162
163     /**
164      * This method allows you to set special values on a URLConnection just before the connect()
165      * method is called. Subclass FOURIResolver and override this method to do things like
166      * adding the user name and password for HTTP basic authentication.
167      * @param connection the URLConnection instance
168      * @param href the original URI
169      */

170     protected void updateURLConnection(URLConnection JavaDoc connection, String JavaDoc href) {
171         //nop
172
}
173     
174     /**
175      * This is a convenience method for users who want to override updateURLConnection for
176      * HTTP basic authentication. Simply call it using the right username and password.
177      * @param connection the URLConnection to set up for HTTP basic authentication
178      * @param username the username
179      * @param password the password
180      */

181     protected void applyHttpBasicAuthentication(URLConnection JavaDoc connection,
182             String JavaDoc username, String JavaDoc password) {
183         String JavaDoc combined = username + ":" + password;
184         try {
185             ByteArrayOutputStream JavaDoc baout = new ByteArrayOutputStream JavaDoc(combined.length() * 2);
186             Base64EncodeStream base64 = new Base64EncodeStream(baout);
187             base64.write(combined.getBytes());
188             base64.close();
189             connection.setRequestProperty("Authorization",
190                     "Basic " + new String JavaDoc(baout.toByteArray()));
191         } catch (IOException JavaDoc e) {
192             //won't happen. We're operating in-memory.
193
throw new RuntimeException JavaDoc("Error during base64 encodation of username/password");
194         }
195     }
196     
197     /**
198      * Returns the base URL as a java.net.URL.
199      * If the base URL is not set a default URL pointing to the
200      * current directory is returned.
201      * @param baseURL the base URL
202      * @returns the base URL as java.net.URL
203      */

204     private URL JavaDoc toBaseURL(String JavaDoc baseURL) {
205         try {
206             return new URL JavaDoc(baseURL == null
207                             ? new java.io.File JavaDoc("").toURL().toExternalForm()
208                             : baseURL);
209         } catch (MalformedURLException JavaDoc mfue) {
210             log.error("Error with base URL \"" + baseURL + "\"): " + mfue.getMessage());
211         }
212         return null;
213     }
214
215     /**
216      * Parses inline data URIs as generated by MS Word's XML export and FO stylesheet.
217      * @see <a HREF="http://www.ietf.org/rfc/rfc2397">RFC 2397</a>
218      */

219     private Source JavaDoc parseDataURI(String JavaDoc href) {
220         int commaPos = href.indexOf(',');
221         // header is of the form data:[<mediatype>][;base64]
222
String JavaDoc header = href.substring(0, commaPos);
223         String JavaDoc data = href.substring(commaPos + 1);
224         if (header.endsWith(";base64")) {
225             byte[] bytes = data.getBytes();
226             ByteArrayInputStream JavaDoc encodedStream = new ByteArrayInputStream JavaDoc(bytes);
227             Base64DecodeStream decodedStream = new Base64DecodeStream(encodedStream);
228             return new StreamSource JavaDoc(decodedStream);
229         } else {
230             //Note that this is not quite the full story here. But since we are only interested
231
//in base64-encoded binary data, the next line will probably never be called.
232
return new StreamSource JavaDoc(new java.io.StringReader JavaDoc(data));
233         }
234     }
235 }
236
Popular Tags