Home | History | Annotate | Download | only in utils
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one
      3  * or more contributor license agreements. See the NOTICE file
      4  * distributed with this work for additional information
      5  * regarding copyright ownership. The ASF licenses this file
      6  * to you under the Apache License, Version 2.0 (the  "License");
      7  * you may not use this file except in compliance with the License.
      8  * You may obtain a copy of the License at
      9  *
     10  *     http://www.apache.org/licenses/LICENSE-2.0
     11  *
     12  * Unless required by applicable law or agreed to in writing, software
     13  * distributed under the License is distributed on an "AS IS" BASIS,
     14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     15  * See the License for the specific language governing permissions and
     16  * limitations under the License.
     17  */
     18 /*
     19  * $Id: SystemIDResolver.java 468655 2006-10-28 07:12:06Z minchau $
     20  */
     21 package org.apache.xml.utils;
     22 
     23 import java.io.File;
     24 
     25 import javax.xml.transform.TransformerException;
     26 
     27 import org.apache.xml.utils.URI.MalformedURIException;
     28 
     29 /**
     30  * This class is used to resolve relative URIs and SystemID
     31  * strings into absolute URIs.
     32  *
     33  * <p>This is a generic utility for resolving URIs, other than the
     34  * fact that it's declared to throw TransformerException.  Please
     35  * see code comments for details on how resolution is performed.</p>
     36  * @xsl.usage internal
     37  */
     38 public class SystemIDResolver
     39 {
     40 
     41   /**
     42    * Get an absolute URI from a given relative URI (local path).
     43    *
     44    * <p>The relative URI is a local filesystem path. The path can be
     45    * absolute or relative. If it is a relative path, it is resolved relative
     46    * to the system property "user.dir" if it is available; if not (i.e. in an
     47    * Applet perhaps which throws SecurityException) then we just return the
     48    * relative path. The space and backslash characters are also replaced to
     49    * generate a good absolute URI.</p>
     50    *
     51    * @param localPath The relative URI to resolve
     52    *
     53    * @return Resolved absolute URI
     54    */
     55   public static String getAbsoluteURIFromRelative(String localPath)
     56   {
     57     if (localPath == null || localPath.length() == 0)
     58       return "";
     59 
     60     // If the local path is a relative path, then it is resolved against
     61     // the "user.dir" system property.
     62     String absolutePath = localPath;
     63     if (!isAbsolutePath(localPath))
     64     {
     65       try
     66       {
     67         absolutePath = getAbsolutePathFromRelativePath(localPath);
     68       }
     69       // user.dir not accessible from applet
     70       catch (SecurityException se)
     71       {
     72         return "file:" + localPath;
     73       }
     74     }
     75 
     76     String urlString;
     77     if (null != absolutePath)
     78     {
     79       if (absolutePath.startsWith(File.separator))
     80         urlString = "file://" + absolutePath;
     81       else
     82         urlString = "file:///" + absolutePath;
     83     }
     84     else
     85       urlString = "file:" + localPath;
     86 
     87     return replaceChars(urlString);
     88   }
     89 
     90   /**
     91    * Return an absolute path from a relative path.
     92    *
     93    * @param relativePath A relative path
     94    * @return The absolute path
     95    */
     96   private static String getAbsolutePathFromRelativePath(String relativePath)
     97   {
     98     return new File(relativePath).getAbsolutePath();
     99   }
    100 
    101   /**
    102    * Return true if the systemId denotes an absolute URI .
    103    *
    104    * @param systemId The systemId string
    105    * @return true if the systemId is an an absolute URI
    106    */
    107   public static boolean isAbsoluteURI(String systemId)
    108   {
    109      /** http://www.ietf.org/rfc/rfc2396.txt
    110       *   Authors should be aware that a path segment which contains a colon
    111       * character cannot be used as the first segment of a relative URI path
    112       * (e.g., "this:that"), because it would be mistaken for a scheme name.
    113      **/
    114      /**
    115       * %REVIEW% Can we assume here that systemId is a valid URI?
    116       * It looks like we cannot ( See discussion of this common problem in
    117       * Bugzilla Bug 22777 ).
    118      **/
    119      //"fix" for Bugzilla Bug 22777
    120     if(isWindowsAbsolutePath(systemId)){
    121         return false;
    122      }
    123 
    124     final int fragmentIndex = systemId.indexOf('#');
    125     final int queryIndex = systemId.indexOf('?');
    126     final int slashIndex = systemId.indexOf('/');
    127     final int colonIndex = systemId.indexOf(':');
    128 
    129     //finding substring  before '#', '?', and '/'
    130     int index = systemId.length() -1;
    131     if(fragmentIndex > 0)
    132         index = fragmentIndex;
    133     if((queryIndex > 0) && (queryIndex <index))
    134         index = queryIndex;
    135     if((slashIndex > 0) && (slashIndex <index))
    136         index = slashIndex;
    137     // return true if there is ':' before '#', '?', and '/'
    138     return ((colonIndex >0) && (colonIndex<index));
    139 
    140   }
    141 
    142   /**
    143    * Return true if the local path is an absolute path.
    144    *
    145    * @param systemId The path string
    146    * @return true if the path is absolute
    147    */
    148   public static boolean isAbsolutePath(String systemId)
    149   {
    150     if(systemId == null)
    151         return false;
    152     final File file = new File(systemId);
    153     return file.isAbsolute();
    154 
    155   }
    156 
    157    /**
    158    * Return true if the local path is a Windows absolute path.
    159    *
    160    * @param systemId The path string
    161    * @return true if the path is a Windows absolute path
    162    */
    163     private static boolean isWindowsAbsolutePath(String systemId)
    164   {
    165     if(!isAbsolutePath(systemId))
    166       return false;
    167     // On Windows, an absolute path starts with "[drive_letter]:\".
    168     if (systemId.length() > 2
    169         && systemId.charAt(1) == ':'
    170         && Character.isLetter(systemId.charAt(0))
    171         && (systemId.charAt(2) == '\\' || systemId.charAt(2) == '/'))
    172       return true;
    173     else
    174       return false;
    175   }
    176 
    177   /**
    178    * Replace spaces with "%20" and backslashes with forward slashes in
    179    * the input string to generate a well-formed URI string.
    180    *
    181    * @param str The input string
    182    * @return The string after conversion
    183    */
    184   private static String replaceChars(String str)
    185   {
    186     StringBuffer buf = new StringBuffer(str);
    187     int length = buf.length();
    188     for (int i = 0; i < length; i++)
    189     {
    190       char currentChar = buf.charAt(i);
    191       // Replace space with "%20"
    192       if (currentChar == ' ')
    193       {
    194         buf.setCharAt(i, '%');
    195         buf.insert(i+1, "20");
    196         length = length + 2;
    197         i = i + 2;
    198       }
    199       // Replace backslash with forward slash
    200       else if (currentChar == '\\')
    201       {
    202         buf.setCharAt(i, '/');
    203       }
    204     }
    205 
    206     return buf.toString();
    207   }
    208 
    209   /**
    210    * Take a SystemID string and try to turn it into a good absolute URI.
    211    *
    212    * @param systemId A URI string, which may be absolute or relative.
    213    *
    214    * @return The resolved absolute URI
    215    */
    216   public static String getAbsoluteURI(String systemId)
    217   {
    218     String absoluteURI = systemId;
    219     if (isAbsoluteURI(systemId))
    220     {
    221       // Only process the systemId if it starts with "file:".
    222       if (systemId.startsWith("file:"))
    223       {
    224         String str = systemId.substring(5);
    225 
    226         // Resolve the absolute path if the systemId starts with "file:///"
    227         // or "file:/". Don't do anything if it only starts with "file://".
    228         if (str != null && str.startsWith("/"))
    229         {
    230           if (str.startsWith("///") || !str.startsWith("//"))
    231           {
    232             // A Windows path containing a drive letter can be relative.
    233             // A Unix path starting with "file:/" is always absolute.
    234             int secondColonIndex = systemId.indexOf(':', 5);
    235             if (secondColonIndex > 0)
    236             {
    237               String localPath = systemId.substring(secondColonIndex-1);
    238               try {
    239                 if (!isAbsolutePath(localPath))
    240                   absoluteURI = systemId.substring(0, secondColonIndex-1) +
    241                                 getAbsolutePathFromRelativePath(localPath);
    242               }
    243               catch (SecurityException se) {
    244                 return systemId;
    245               }
    246             }
    247           }
    248         }
    249         else
    250         {
    251           return getAbsoluteURIFromRelative(systemId.substring(5));
    252         }
    253 
    254         return replaceChars(absoluteURI);
    255       }
    256       else
    257         return systemId;
    258     }
    259     else
    260       return getAbsoluteURIFromRelative(systemId);
    261 
    262   }
    263 
    264 
    265   /**
    266    * Take a SystemID string and try to turn it into a good absolute URI.
    267    *
    268    * @param urlString SystemID string
    269    * @param base The URI string used as the base for resolving the systemID
    270    *
    271    * @return The resolved absolute URI
    272    * @throws TransformerException thrown if the string can't be turned into a URI.
    273    */
    274   public static String getAbsoluteURI(String urlString, String base)
    275           throws TransformerException
    276   {
    277     if (base == null)
    278       return getAbsoluteURI(urlString);
    279 
    280     String absoluteBase = getAbsoluteURI(base);
    281     URI uri = null;
    282     try
    283     {
    284       URI baseURI = new URI(absoluteBase);
    285       uri = new URI(baseURI, urlString);
    286     }
    287     catch (MalformedURIException mue)
    288     {
    289       throw new TransformerException(mue);
    290     }
    291 
    292     return replaceChars(uri.toString());
    293   }
    294 
    295 }
    296