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