001: /*
002: * Copyright 1999-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: /*
017: * $Id: SystemIDResolver.java,v 1.1 2004/10/14 18:30:53 minchau Exp $
018: */
019: package org.apache.xml.serializer.utils;
020:
021: import java.io.File;
022:
023: import javax.xml.transform.TransformerException;
024:
025: import org.apache.xml.serializer.utils.URI.MalformedURIException;
026:
027: /**
028: * This class is used to resolve relative URIs and SystemID
029: * strings into absolute URIs.
030: *
031: * <p>This is a generic utility for resolving URIs, other than the
032: * fact that it's declared to throw TransformerException. Please
033: * see code comments for details on how resolution is performed.</p>
034: *
035: * This class is a copy of the one in org.apache.xml.utils.
036: * It exists to cut the serializers dependancy on that package.
037: *
038: * This class is not a public API, it is only public because it is
039: * used in org.apache.xml.serializer.
040: *
041: * @xsl.usage internal
042: */
043: public final class SystemIDResolver {
044:
045: /**
046: * Get an absolute URI from a given relative URI (local path).
047: *
048: * <p>The relative URI is a local filesystem path. The path can be
049: * absolute or relative. If it is a relative path, it is resolved relative
050: * to the system property "user.dir" if it is available; if not (i.e. in an
051: * Applet perhaps which throws SecurityException) then we just return the
052: * relative path. The space and backslash characters are also replaced to
053: * generate a good absolute URI.</p>
054: *
055: * @param localPath The relative URI to resolve
056: *
057: * @return Resolved absolute URI
058: */
059: public static String getAbsoluteURIFromRelative(String localPath) {
060: if (localPath == null || localPath.length() == 0)
061: return "";
062:
063: // If the local path is a relative path, then it is resolved against
064: // the "user.dir" system property.
065: String absolutePath = localPath;
066: if (!isAbsolutePath(localPath)) {
067: try {
068: absolutePath = getAbsolutePathFromRelativePath(localPath);
069: }
070: // user.dir not accessible from applet
071: catch (SecurityException se) {
072: return "file:" + localPath;
073: }
074: }
075:
076: String urlString;
077: if (null != absolutePath) {
078: if (absolutePath.startsWith(File.separator))
079: urlString = "file://" + absolutePath;
080: else
081: urlString = "file:///" + absolutePath;
082: } else
083: urlString = "file:" + localPath;
084:
085: return replaceChars(urlString);
086: }
087:
088: /**
089: * Return an absolute path from a relative path.
090: *
091: * @param relativePath A relative path
092: * @return The absolute path
093: */
094: private static String getAbsolutePathFromRelativePath(
095: String relativePath) {
096: return new File(relativePath).getAbsolutePath();
097: }
098:
099: /**
100: * Return true if the systemId denotes an absolute URI .
101: *
102: * @param systemId The systemId string
103: * @return true if the systemId is an an absolute URI
104: */
105: public static boolean isAbsoluteURI(String systemId) {
106: /** http://www.ietf.org/rfc/rfc2396.txt
107: * Authors should be aware that a path segment which contains a colon
108: * character cannot be used as the first segment of a relative URI path
109: * (e.g., "this:that"), because it would be mistaken for a scheme name.
110: **/
111: /**
112: * %REVIEW% Can we assume here that systemId is a valid URI?
113: * It looks like we cannot ( See discussion of this common problem in
114: * Bugzilla Bug 22777 ).
115: **/
116: //"fix" for Bugzilla Bug 22777
117: if (isWindowsAbsolutePath(systemId)) {
118: return false;
119: }
120:
121: final int fragmentIndex = systemId.indexOf('#');
122: final int queryIndex = systemId.indexOf('?');
123: final int slashIndex = systemId.indexOf('/');
124: final int colonIndex = systemId.indexOf(':');
125:
126: //finding substring before '#', '?', and '/'
127: int index = systemId.length() - 1;
128: if (fragmentIndex > 0)
129: index = fragmentIndex;
130: if ((queryIndex > 0) && (queryIndex < index))
131: index = queryIndex;
132: if ((slashIndex > 0) && (slashIndex < index))
133: index = slashIndex;
134: // return true if there is ':' before '#', '?', and '/'
135: return ((colonIndex > 0) && (colonIndex < index));
136:
137: }
138:
139: /**
140: * Return true if the local path is an absolute path.
141: *
142: * @param systemId The path string
143: * @return true if the path is absolute
144: */
145: public static boolean isAbsolutePath(String systemId) {
146: if (systemId == null)
147: return false;
148: final File file = new File(systemId);
149: return file.isAbsolute();
150:
151: }
152:
153: /**
154: * Return true if the local path is a Windows absolute path.
155: *
156: * @param systemId The path string
157: * @return true if the path is a Windows absolute path
158: */
159: private static boolean isWindowsAbsolutePath(String systemId) {
160: if (!isAbsolutePath(systemId))
161: return false;
162: // On Windows, an absolute path starts with "[drive_letter]:\".
163: if (systemId.length() > 2
164: && systemId.charAt(1) == ':'
165: && Character.isLetter(systemId.charAt(0))
166: && (systemId.charAt(2) == '\\' || systemId.charAt(2) == '/'))
167: return true;
168: else
169: return false;
170: }
171:
172: /**
173: * Replace spaces with "%20" and backslashes with forward slashes in
174: * the input string to generate a well-formed URI string.
175: *
176: * @param str The input string
177: * @return The string after conversion
178: */
179: private static String replaceChars(String str) {
180: StringBuffer buf = new StringBuffer(str);
181: int length = buf.length();
182: for (int i = 0; i < length; i++) {
183: char currentChar = buf.charAt(i);
184: // Replace space with "%20"
185: if (currentChar == ' ') {
186: buf.setCharAt(i, '%');
187: buf.insert(i + 1, "20");
188: length = length + 2;
189: i = i + 2;
190: }
191: // Replace backslash with forward slash
192: else if (currentChar == '\\') {
193: buf.setCharAt(i, '/');
194: }
195: }
196:
197: return buf.toString();
198: }
199:
200: /**
201: * Take a SystemID string and try to turn it into a good absolute URI.
202: *
203: * @param systemId A URI string, which may be absolute or relative.
204: *
205: * @return The resolved absolute URI
206: */
207: public static String getAbsoluteURI(String systemId) {
208: String absoluteURI = systemId;
209: if (isAbsoluteURI(systemId)) {
210: // Only process the systemId if it starts with "file:".
211: if (systemId.startsWith("file:")) {
212: String str = systemId.substring(5);
213:
214: // Resolve the absolute path if the systemId starts with "file:///"
215: // or "file:/". Don't do anything if it only starts with "file://".
216: if (str != null && str.startsWith("/")) {
217: if (str.startsWith("///") || !str.startsWith("//")) {
218: // A Windows path containing a drive letter can be relative.
219: // A Unix path starting with "file:/" is always absolute.
220: int secondColonIndex = systemId.indexOf(':', 5);
221: if (secondColonIndex > 0) {
222: String localPath = systemId
223: .substring(secondColonIndex - 1);
224: try {
225: if (!isAbsolutePath(localPath))
226: absoluteURI = systemId.substring(0,
227: secondColonIndex - 1)
228: + getAbsolutePathFromRelativePath(localPath);
229: } catch (SecurityException se) {
230: return systemId;
231: }
232: }
233: }
234: } else {
235: return getAbsoluteURIFromRelative(systemId
236: .substring(5));
237: }
238:
239: return replaceChars(absoluteURI);
240: } else
241: return systemId;
242: } else
243: return getAbsoluteURIFromRelative(systemId);
244:
245: }
246:
247: /**
248: * Take a SystemID string and try to turn it into a good absolute URI.
249: *
250: * @param urlString SystemID string
251: * @param base The URI string used as the base for resolving the systemID
252: *
253: * @return The resolved absolute URI
254: * @throws TransformerException thrown if the string can't be turned into a URI.
255: */
256: public static String getAbsoluteURI(String urlString, String base)
257: throws TransformerException {
258: if (base == null)
259: return getAbsoluteURI(urlString);
260:
261: String absoluteBase = getAbsoluteURI(base);
262: URI uri = null;
263: try {
264: URI baseURI = new URI(absoluteBase);
265: uri = new URI(baseURI, urlString);
266: } catch (MalformedURIException mue) {
267: throw new TransformerException(mue);
268: }
269:
270: return replaceChars(uri.toString());
271: }
272:
273: }
|