001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018 /*
019 * $Id: SystemIDResolver.java 468655 2006-10-28 07:12:06Z minchau $
020 */
021 package org.apache.xml.utils;
022
023 import java.io.File;
024
025 import javax.xml.transform.TransformerException;
026
027 import org.apache.xml.utils.URI.MalformedURIException;
028
029 /**
030 * This class is used to resolve relative URIs and SystemID
031 * strings into absolute URIs.
032 *
033 * <p>This is a generic utility for resolving URIs, other than the
034 * fact that it's declared to throw TransformerException. Please
035 * see code comments for details on how resolution is performed.</p>
036 * @xsl.usage internal
037 */
038 public class SystemIDResolver
039 {
040
041 /**
042 * Get an absolute URI from a given relative URI (local path).
043 *
044 * <p>The relative URI is a local filesystem path. The path can be
045 * absolute or relative. If it is a relative path, it is resolved relative
046 * to the system property "user.dir" if it is available; if not (i.e. in an
047 * Applet perhaps which throws SecurityException) then we just return the
048 * relative path. The space and backslash characters are also replaced to
049 * generate a good absolute URI.</p>
050 *
051 * @param localPath The relative URI to resolve
052 *
053 * @return Resolved absolute URI
054 */
055 public static String getAbsoluteURIFromRelative(String localPath)
056 {
057 if (localPath == null || localPath.length() == 0)
058 return "";
059
060 // If the local path is a relative path, then it is resolved against
061 // the "user.dir" system property.
062 String absolutePath = localPath;
063 if (!isAbsolutePath(localPath))
064 {
065 try
066 {
067 absolutePath = getAbsolutePathFromRelativePath(localPath);
068 }
069 // user.dir not accessible from applet
070 catch (SecurityException se)
071 {
072 return "file:" + localPath;
073 }
074 }
075
076 String urlString;
077 if (null != absolutePath)
078 {
079 if (absolutePath.startsWith(File.separator))
080 urlString = "file://" + absolutePath;
081 else
082 urlString = "file:///" + absolutePath;
083 }
084 else
085 urlString = "file:" + localPath;
086
087 return replaceChars(urlString);
088 }
089
090 /**
091 * Return an absolute path from a relative path.
092 *
093 * @param relativePath A relative path
094 * @return The absolute path
095 */
096 private static String getAbsolutePathFromRelativePath(String relativePath)
097 {
098 return new File(relativePath).getAbsolutePath();
099 }
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 }