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