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 468654 2006-10-28 07:09:23Z minchau $ 020 */ 021 package org.apache.xml.serializer.utils; 022 023 import java.io.File; 024 025 import javax.xml.transform.TransformerException; 026 027 import org.apache.xml.serializer.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 * 037 * This class is a copy of the one in org.apache.xml.utils. 038 * It exists to cut the serializers dependancy on that package. 039 * 040 * This class is not a public API, it is only public because it is 041 * used in org.apache.xml.serializer. 042 * 043 * @xsl.usage internal 044 */ 045 public final class SystemIDResolver 046 { 047 048 /** 049 * Get an absolute URI from a given relative URI (local path). 050 * 051 * <p>The relative URI is a local filesystem path. The path can be 052 * absolute or relative. If it is a relative path, it is resolved relative 053 * to the system property "user.dir" if it is available; if not (i.e. in an 054 * Applet perhaps which throws SecurityException) then we just return the 055 * relative path. The space and backslash characters are also replaced to 056 * generate a good absolute URI.</p> 057 * 058 * @param localPath The relative URI to resolve 059 * 060 * @return Resolved absolute URI 061 */ 062 public static String getAbsoluteURIFromRelative(String localPath) 063 { 064 if (localPath == null || localPath.length() == 0) 065 return ""; 066 067 // If the local path is a relative path, then it is resolved against 068 // the "user.dir" system property. 069 String absolutePath = localPath; 070 if (!isAbsolutePath(localPath)) 071 { 072 try 073 { 074 absolutePath = getAbsolutePathFromRelativePath(localPath); 075 } 076 // user.dir not accessible from applet 077 catch (SecurityException se) 078 { 079 return "file:" + localPath; 080 } 081 } 082 083 String urlString; 084 if (null != absolutePath) 085 { 086 if (absolutePath.startsWith(File.separator)) 087 urlString = "file://" + absolutePath; 088 else 089 urlString = "file:///" + absolutePath; 090 } 091 else 092 urlString = "file:" + localPath; 093 094 return replaceChars(urlString); 095 } 096 097 /** 098 * Return an absolute path from a relative path. 099 * 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 }