001: // Copyright (c) 2007 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ../../COPYING.
003:
004: package gnu.text;
005:
006: import java.io.*;
007: import java.net.*;
008: import gnu.mapping.*;
009:
010: /** A generalized path/location, including File and URIs. */
011:
012: public abstract class Path
013: // Possibly FUTURE: #ifdef JAVA6: implements javax.tools.FileObject
014: {
015: /** This is equivalent to the System {@code "user.dir} property.
016: * However, the getProperty is tracked dynamically and resolved
017: * as needed. */
018: public static final FilePath userDirPath = FilePath
019: .valueOf(new File("."));
020:
021: public static Path defaultPath = userDirPath;
022:
023: /* #ifdef JAVA2 */
024: private static ThreadLocal pathLocation = new ThreadLocal();
025:
026: /* #endif */
027:
028: protected Path() {
029: }
030:
031: public static Path currentPath() {
032: /* #ifdef JAVA2 */
033: Object path = pathLocation.get();
034: if (path != null)
035: return (Path) path;
036: /* #endif */
037: return defaultPath;
038: }
039:
040: public static void setCurrentPath(Path path) {
041: /* #ifdef JAVA2 */
042: pathLocation.set(path);
043: /* #else */
044: // defaultPath = path;
045: /* #endif */
046: }
047:
048: public static Path coerceToPathOrNull(Object path) {
049: if (path instanceof Path)
050: return (Path) path;
051: if (path instanceof URL)
052: return URLPath.valueOf((URL) path);
053: /* #ifdef use:java.net.URI */
054: if (path instanceof URI)
055: return URIPath.valueOf((URI) path);
056: /* #endif */
057: if (path instanceof File)
058: return FilePath.valueOf((File) path);
059: String str;
060: if (path instanceof gnu.lists.FString) // FIXME: || UntypedAtomic
061: str = path.toString();
062: else if (!(path instanceof String))
063: return null;
064: else
065: str = (String) path;
066: if (Path.uriSchemeSpecified(str))
067: return URIPath.valueOf(str);
068: else
069: return FilePath.valueOf(str);
070: }
071:
072: public static Path valueOf(Object arg) {
073: Path path = coerceToPathOrNull(arg);
074: if (path == null)
075: throw new WrongType((String) null, WrongType.ARG_CAST, arg,
076: "path");
077: return path;
078: }
079:
080: public static URL toURL(String str) {
081: try {
082: if (!Path.uriSchemeSpecified(str)) {
083: Path cur = currentPath();
084: Path path = cur.resolve(str);
085: if (path.isAbsolute())
086: return path.toURL();
087: str = path.toString();
088: }
089: return new URL(str);
090: } catch (Throwable ex) {
091: throw WrappedException.wrapIfNeeded(ex);
092: }
093: }
094:
095: /** Helper routine to get the scheme part of a URI.
096: * The scheme part is "http:" or "file:" or "ftp:" most commonly.
097: * This functions searches for the first ':' that doesn't follow a '/'.
098: * @return The length of the scheme component, not counting the colon,
099: * (or alternatively the index of the colon), or -1 if the is no scheme.
100: */
101: public static int uriSchemeLength(String uri) {
102: int len = uri.length();
103: for (int i = 0; i < len; i++) {
104: char ch = uri.charAt(i);
105: if (ch == ':')
106: return i;
107: if (i == 0 ? !Character.isLetter(ch) : (!Character
108: .isLetterOrDigit(ch)
109: && ch != '+' && ch != '-' && ch != '.'))
110: return -1;
111: }
112: return -1;
113: }
114:
115: /** Tests if a URL has a scheme.
116: * For convenience, we treat a 1-character "scheme" as an
117: * MS-DOS-style "drive letter" - i.e. not a scheme. */
118: public static boolean uriSchemeSpecified(String name) {
119: int ulen = uriSchemeLength(name);
120: if (ulen == 1 && File.separatorChar == '\\') {
121: char drive = name.charAt(0);
122: return !((drive >= 'a' && drive <= 'z') || (drive >= 'A' && drive <= 'Z'));
123: }
124: return ulen > 0;
125: }
126:
127: public abstract boolean isAbsolute();
128:
129: /** Does this path name a directory?
130: * The default implementation returns true only if the path ends
131: * with '/' or the separatorChar.
132: */
133: public boolean isDirectory() {
134: String str = toString();
135: int len = str.length();
136: if (len > 0) {
137: char last = str.charAt(len - 1);
138: if (last == '/' || last == File.separatorChar)
139: return true;
140: }
141: return false;
142: }
143:
144: public boolean exists() {
145: return getLastModified() != 0;
146: }
147:
148: public abstract long getLastModified();
149:
150: public long getContentLength() {
151: return -1;
152: }
153:
154: public abstract String getScheme();
155:
156: public String getAuthority() {
157: return null;
158: }
159:
160: public String getUserInfo() {
161: return null;
162: }
163:
164: public String getHost() {
165: return null;
166: }
167:
168: public abstract String getPath();
169:
170: public Path getDirectory() {
171: if (isDirectory())
172: return this ;
173: else
174: return resolve("");
175: }
176:
177: public Path getParent() {
178: return resolve(isDirectory() ? ".." : "");
179: }
180:
181: public String getLast() {
182: String p = getPath();
183: if (p == null)
184: return null;
185: int len = p.length();
186: int end = len;
187: for (int i = len;;) {
188: if (--i <= 0)
189: return "";
190: char c = p.charAt(i);
191: if (c == '/'
192: || (this instanceof FilePath && c == File.separatorChar)) {
193: if (i + 1 == len)
194: end = i;
195: else
196: return p.substring(i + 1, end);
197: }
198: }
199: }
200:
201: public String getExtension() {
202: String p = getPath();
203: if (p == null)
204: return null;
205: int len = p.length();
206: for (int i = len;;) {
207: if (--i <= 0)
208: return null;
209: char c = p.charAt(i);
210: boolean sawDot = false;
211: if (c == '.') {
212: c = p.charAt(i - 1);
213: sawDot = true;
214: }
215: if (c == '/'
216: || (this instanceof FilePath && c == File.separatorChar))
217: return null;
218: if (sawDot)
219: return p.substring(i + 1);
220: }
221: }
222:
223: public int getPort() {
224: return -1;
225: }
226:
227: public String getQuery() {
228: return null;
229: }
230:
231: public String getFragment() {
232: return null;
233: }
234:
235: public abstract URL toURL();
236:
237: /* #ifdef use:java.net.URI */
238: public abstract URI toURI();
239:
240: /* #else */
241: // public String toURI () { return toURIString(); }
242: /* #endif */
243: public String toURIString() {
244: return toURI().toString();
245: }
246:
247: public Path resolve(Path relative) {
248: if (relative.isAbsolute())
249: return relative;
250: return resolve(relative.toString());
251: }
252:
253: public abstract Path resolve(String relative);
254:
255: public static InputStream openInputStream(Object uri)
256: throws IOException {
257: return Path.valueOf(uri).openInputStream();
258: }
259:
260: public abstract InputStream openInputStream() throws IOException;
261:
262: public abstract OutputStream openOutputStream() throws IOException;
263:
264: /** Convert an absolute URI to one relatve to a given base.
265: * This goes beyond java.net.URI.relativize in that if the arguments
266: * have a common prefix, it can create a relative URI using "../" steps.
267: */
268: public static String relativize(String in, String base)
269: throws java.net.URISyntaxException, java.io.IOException {
270: String baseStr = base;
271: String inStr = in;
272: /* #ifdef use:java.net.URI */
273: baseStr = new URI(baseStr).normalize().toString();
274: inStr = URLPath.valueOf(in).toURI().normalize().toString();
275: /* #endif */
276: int baseLen = baseStr.length();
277: int inLen = inStr.length();
278: int i = 0;
279: int sl = 0;
280: int colon = 0;
281: for (; i < baseLen && i < inLen; i++) {
282: char cb = baseStr.charAt(i);
283: char ci = inStr.charAt(i);
284: if (cb != ci)
285: break;
286: if (cb == '/')
287: sl = i;
288: if (cb == ':')
289: colon = i;
290: }
291: if (colon > 0
292: && (sl > colon + 2 || baseLen <= colon + 2 || baseStr
293: .charAt(colon + 2) != '/')
294: /*
295: && (colon + 2 != CLASS_RESOURCE_URI_PREFIX_LENGTH
296: || ! inStr.substring(0, colon + 2).equals(CLASS_RESOURCE_URI_PREFIX)
297: || getClassLoaderForURI(base) == getClassLoaderForURI(in))
298: */
299: ) {
300: baseStr = baseStr.substring(sl + 1);
301: inStr = inStr.substring(sl + 1);
302: } else
303: return in;
304: /* #ifdef JAVA5 */
305: // StringBuilder sbuf = new StringBuilder();
306: /* #else */
307: StringBuffer sbuf = new StringBuffer();
308: /* #endif */
309: sl = 0;
310: for (i = baseLen = baseStr.length(); --i >= 0;)
311: if (baseStr.charAt(i) == '/') // sep?
312: sbuf.append("../");
313: sbuf.append(inStr);
314: return sbuf.toString();
315: }
316:
317: public Path getAbsolute() {
318: if (this == Path.userDirPath)
319: return resolve("");
320: else
321: return currentPath().resolve(this );
322: }
323:
324: public Path getCanonical() {
325: return getAbsolute();
326: }
327: }
|