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