1 /**
2 * Redistribution and use of this software and associated documentation
3 * ("Software"), with or without modification, are permitted provided
4 * that the following conditions are met:
5 *
6 * 1. Redistributions of source code must retain copyright
7 * statements and notices. Redistributions must also contain a
8 * copy of this document.
9 *
10 * 2. Redistributions in binary form must reproduce the
11 * above copyright notice, this list of conditions and the
12 * following disclaimer in the documentation and/or other
13 * materials provided with the distribution.
14 *
15 * 3. The name "Exolab" must not be used to endorse or promote
16 * products derived from this Software without prior written
17 * permission of Intalio, Inc. For written permission,
18 * please contact info@exolab.org.
19 *
20 * 4. Products derived from this Software may not be called "Exolab"
21 * nor may "Exolab" appear in their names without prior written
22 * permission of Intalio, Inc. Exolab is a registered
23 * trademark of Intalio, Inc.
24 *
25 * 5. Due credit should be given to the Exolab Project
26 * (http://www.exolab.org/).
27 *
28 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS
29 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32 * INTALIO, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39 * OF THE POSSIBILITY OF SUCH DAMAGE.
40 *
41 * Copyright 2002-2003 (C) Intalio, Inc. All Rights Reserved.
42 *
43 * $Id$
44 */
45 package org.exolab.castor.net.util;
46
47
48 import java.io.*;
49 import java.net.URL;
50 import java.net.MalformedURLException;
51 import java.util.Stack;
52 import java.util.StringTokenizer;
53
54 /**
55 * A utility class for URI handling
56 *
57 * @author <a href="mailto:kvisco-at-intalio.com">Keith Visco</a>
58 **/
59 public class URIUtils {
60
61 /**
62 * the File protocol
63 **/
64 private static final String FILE_PROTOCOL_PREFIX = "file:///";
65
66 /**
67 * the path separator for an URI
68 */
69 private static final char HREF_PATH_SEP = '/';
70
71 /**
72 * the path separate for a URL as a String
73 */
74 private static final String URL_PATH_SEP_STR = "/";
75
76 /**
77 * The current directory designator
78 */
79 private static final String CURRENT_DIR_OP = ".";
80
81 /**
82 * The parent directory designator
83 */
84 private static final String PARENT_DIR_OP = "..";
85
86 /**
87 * Returns an InputStream for the file represented by the href
88 * argument
89 * @param href the href of the file to get the input stream for.
90 * @param documentBase the document base of the href argument, if it
91 * is a relative href
92 * set documentBase to null if there is none.
93 * @return an InputStream to the desired resource
94 * @throws java.io.FileNotFoundException when the file could not be
95 * found
96 * @throws java.io.IOException
97 */
98 public static InputStream getInputStream(String href, String documentBase)
99 throws java.io.FileNotFoundException, java.io.IOException
100 {
101
102 //-- check for absolute url
103 URL url = null;
104 try {
105 url = new URL(href);
106 return url.openStream();
107 }
108 catch (MalformedURLException muex) {}
109
110 //-- join document base + href
111 String xHref = null;
112 if ((documentBase != null) && (documentBase.length() > 0)) {
113 int idx = documentBase.lastIndexOf(HREF_PATH_SEP);
114 if (idx == (documentBase.length()-1))
115 xHref = documentBase+href;
116 else
117 xHref = documentBase+HREF_PATH_SEP+href;
118 }
119 else xHref = href;
120
121 //-- check for relative url
122 try {
123 url = new URL(xHref);
124 return url.openStream();
125 }
126 catch(MalformedURLException muex) {}
127
128 // Try local files
129 File iFile = new File(href);
130 if (iFile.isAbsolute()) return new FileInputStream(iFile);
131
132 iFile = new File(xHref);
133 return new FileInputStream(iFile);
134
135 } //-- getInputStream
136
137 /**
138 * Returns a Reader for the file represented by the href
139 * argument
140 * @param href the href of the file to get the input stream for.
141 * @param documentBase the document base of the href argument, if it
142 * is a relative href
143 * set documentBase to null if there is none.
144 * @return an InputStream to the desired resource
145 * @exception java.io.FileNotFoundException when the file could not be
146 * found
147 **/
148 public static Reader getReader(String href, String documentBase)
149 throws java.io.FileNotFoundException, java.io.IOException
150 {
151 InputStream is = getInputStream(href, documentBase);
152 return new InputStreamReader(is);
153 } //-- getReader
154
155 /**
156 * Returns the document base of the href argument
157 * @return the document base of the given href
158 **/
159 public static String getDocumentBase(String href) {
160
161 String docBase = "";
162
163 if (href == null) return docBase;
164
165 int idx = -1;
166 //-- check for URL
167 try {
168 //-- try to create a new URL and see if MalformedURLExcetion is
169 //-- ever thrown
170 new URL(href);
171 idx = href.lastIndexOf(HREF_PATH_SEP);
172 }
173 catch(MalformedURLException muex) {
174 //-- The following contains a fix from Shane Hathaway
175 //-- to handle the case when both "\" and "/" appear in filename
176 int idx2 = href.lastIndexOf(HREF_PATH_SEP);
177 idx = href.lastIndexOf(File.separator);
178 if (idx2 > idx) idx = idx2;
179 }
180
181 if (idx >= 0) docBase = href.substring(0,idx);
182
183 return docBase;
184 } //-- getDocumentBase
185
186 /**
187 * Returns the relative URI of the href argument
188 *
189 * @return the relative URI the given href
190 **/
191 public static String getRelativeURI(String href) {
192
193 if (href == null) return href;
194
195 int idx = -1;
196 //-- check for URL
197 try {
198 //-- try to create a new URL and see if MalformedURLExcetion is
199 //-- ever thrown
200 new URL(href);
201 idx = href.lastIndexOf(HREF_PATH_SEP);
202 }
203 catch(MalformedURLException muex) {
204 //-- The following contains a fix from Shane Hathaway
205 //-- to handle the case when both "\" and "/" appear in filename
206 int idx2 = href.lastIndexOf(HREF_PATH_SEP);
207 idx = href.lastIndexOf(File.separator);
208 if (idx2 > idx) idx = idx2;
209 }
210
211 if (idx >= 0)
212 return href.substring(idx+1);
213
214 return href;
215 } //-- getRelativeURI
216
217 /**
218 * This method removes "." or ".." from absolute URL.
219 * I needed this method because the JDK doesn't do this
220 * automatically when creating URLs.
221 *
222 * @param absoluteURL the absolute URI to normalize
223 */
224 public static String normalize(String absoluteURL)
225 throws MalformedURLException
226 {
227 if (absoluteURL == null) return absoluteURL;
228 if (absoluteURL.indexOf('.') < 0) return absoluteURL;
229
230 //-- Note: using StringTokenizer and Stacks
231 //-- is not very efficient, this may need
232 //-- some optimizing
233 Stack tokens = new Stack();
234 StringTokenizer st = new StringTokenizer(absoluteURL, URL_PATH_SEP_STR, true);
235 String last = null;
236 while (st.hasMoreTokens()) {
237 String token = st.nextToken();
238 if (URL_PATH_SEP_STR.equals(token)) {
239 if (URL_PATH_SEP_STR.equals(last)) {
240 tokens.push("");
241 }
242 }
243 else if (PARENT_DIR_OP.equals(token)) {
244 if (tokens.empty()) {
245 //-- this should be an error
246 throw new MalformedURLException("invalid absolute URL: " + absoluteURL);
247 }
248 tokens.pop();
249 }
250 else {
251 if (!CURRENT_DIR_OP.equals(token)) {
252 tokens.push(token);
253 }
254 }
255 last = token;
256 }
257
258 //-- rebuild URL
259 StringBuffer buffer = new StringBuffer(absoluteURL.length());
260 for (int i = 0; i < tokens.size(); i++) {
261 if (i > 0) buffer.append(HREF_PATH_SEP);
262 buffer.append(tokens.elementAt(i).toString());
263 }
264 return buffer.toString();
265 } //-- normalize
266
267
268 /**
269 *
270 **/
271 public static String resolveAsString(String href, String documentBase) {
272
273 try {
274 //-- try to create a new URL and see if MalformedURLExcetion is
275 //-- ever thrown
276 new URL(href);
277 return href;
278 }
279 catch(MalformedURLException muex) {}
280
281
282 //-- join document base + href
283 String absolute = null;
284 if ((documentBase != null) && (documentBase.length() > 0)) {
285 int idx = documentBase.lastIndexOf(HREF_PATH_SEP);
286 if (idx == (documentBase.length()-1))
287 absolute = documentBase+href;
288 else
289 absolute = documentBase+HREF_PATH_SEP+href;
290
291
292 }
293 else absolute = href;
294
295
296 try {
297 //-- try to create a new URL and see if MalformedURLExcetion is
298 //-- ever thrown
299
300 if (absolute.indexOf("./") >= 0) {
301 //-- normalize . or .. from URL
302 absolute = normalize(absolute);
303 }
304 new URL(absolute);
305 return absolute;
306 }
307 catch(MalformedURLException muex) {
308 //-- check for unrecognized protocol
309 int idx = absolute.indexOf(':');
310 if (idx >= 0) {
311 String scheme = absolute.substring(0, idx);
312 //-- a bit of a hack, but good enough for now
313 String error = "unknown protocol: " + scheme;
314 if (error.equals(muex.getMessage())) {
315 return absolute;
316 }
317 }
318
319 }
320
321
322 // Try local files
323 String fileURL = absolute;
324 File iFile = new File(href);
325 boolean exists = iFile.exists();
326 fileURL = createFileURL(iFile.getAbsolutePath());
327 if (!iFile.isAbsolute()) {
328 iFile = new File(absolute);
329 if (iFile.exists() || (!exists)) {
330 fileURL = createFileURL(iFile.getAbsolutePath());
331 }
332 }
333
334 //-- one last sanity check
335 try {
336 //-- try to create a new URL and see if MalformedURLExcetion is
337 //-- ever thrown
338 new URL(fileURL);
339 return fileURL;
340 }
341 catch(MalformedURLException muex) {}
342
343 //-- At this point we we're unsucessful at trying to resolve
344 //-- the href + documentbase, this could be due to a custom
345 //-- protocol or typo in the URI, just return documentBase +
346 //-- href
347 return absolute;
348 } //-- resolveHref
349
350 /**
351 * Creates a File URL for the given file name
352 *
353 * @param filename the name of the file
354 * @return the String representation of the File URL
355 **/
356 private static String createFileURL(String filename) {
357
358 if (filename == null) return FILE_PROTOCOL_PREFIX;
359 int size = filename.length() + FILE_PROTOCOL_PREFIX.length();
360 StringBuffer sb = new StringBuffer(size);
361 sb.append(FILE_PROTOCOL_PREFIX);
362 char[] chars = filename.toCharArray();
363 for (int i = 0; i < chars.length; i++) {
364 char ch = chars[i];
365 switch (ch) {
366 case '\\':
367 sb.append(HREF_PATH_SEP);
368 break;
369 default:
370 sb.append(ch);
371 break;
372
373 }
374 }
375 return sb.toString();
376 } //-- createFileURL
377
378
379 } //-- URIUtils