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 }