View Javadoc
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