001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.vfs;
031:
032: import com.caucho.util.CharBuffer;
033:
034: import java.io.File;
035: import java.io.FileInputStream;
036: import java.io.FileNotFoundException;
037: import java.io.FileOutputStream;
038: import java.io.IOException;
039: import java.io.RandomAccessFile;
040: import java.util.Map;
041:
042: /**
043: * FilePath implements the native filesystem.
044: */
045: public class FilePath extends FilesystemPath {
046: // The underlying Java File object.
047: private static byte[] NEWLINE = getNewlineString().getBytes();
048:
049: private static FilesystemPath PWD;
050:
051: private File _file;
052:
053: /**
054: * @param path canonical path
055: */
056: protected FilePath(FilesystemPath root, String userPath, String path) {
057: super (root, userPath, path);
058:
059: _separatorChar = getFileSeparatorChar();
060: }
061:
062: public FilePath(String path) {
063: this (null, //PWD != null ? PWD._root : null,
064: path, normalizePath("/", initialPath(path), 0,
065: getFileSeparatorChar()));
066:
067: if (_root == null) {
068: _root = new FilePath(null, "/", "/");
069: _root._root = _root;
070:
071: if (PWD == null)
072: PWD = _root;
073: }
074:
075: _separatorChar = _root._separatorChar;
076: }
077:
078: protected static String initialPath(String path) {
079: if (path == null)
080: return getPwd();
081: else if (path.length() > 0 && path.charAt(0) == '/')
082: return path;
083: else if (path.length() > 1 && path.charAt(1) == ':'
084: && isWindows())
085: //return convertFromWindowsPath(path);
086: return path;
087: else {
088: String dir = getPwd();
089:
090: if (dir.length() > 0 && dir.charAt(dir.length() - 1) == '/')
091: return dir + path;
092: else
093: return dir + "/" + path;
094: }
095: }
096:
097: /**
098: * Gets the system's user dir (pwd) and convert it to the Resin format.
099: */
100: public static String getPwd() {
101: String path = getUserDir();
102:
103: path = path.replace(getFileSeparatorChar(), '/');
104:
105: if (isWindows())
106: path = convertFromWindowsPath(path);
107:
108: return path;
109: }
110:
111: /**
112: * a:xxx -> /a:xxx
113: * ///a:xxx -> /a:xxx
114: * //xxx -> /:/xxx
115: *
116: */
117: private static String convertFromWindowsPath(String path) {
118: int colon = path.indexOf(':');
119: int length = path.length();
120: char ch;
121:
122: if (colon == 1 && (ch = path.charAt(0)) != '/' && ch != '\\')
123: return "/" + path.charAt(0) + ":/" + path.substring(2);
124: else if (length > 1
125: && ((ch = path.charAt(0)) == '/' || ch == '\\')
126: && ((ch = path.charAt(1)) == '/' || ch == '\\')) {
127: if (colon < 0)
128: return "/:" + path;
129:
130: for (int i = colon - 2; i > 1; i--) {
131: if ((ch = path.charAt(i)) != '/' && ch != '\\')
132: return "/:" + path;
133: }
134:
135: ch = path.charAt(colon - 1);
136:
137: if (ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z')
138: return path.substring(colon - 2);
139: else
140: return "/:" + path;
141: } else
142: return path;
143: }
144:
145: /**
146: * Lookup the path, handling windows weirdness
147: */
148: protected Path schemeWalk(String userPath,
149: Map<String, Object> attributes, String filePath, int offset) {
150: if (!isWindows())
151: return super .schemeWalk(userPath, attributes, filePath,
152: offset);
153:
154: String canonicalPath;
155:
156: if (filePath.length() < offset + 2)
157: return super .schemeWalk(userPath, attributes, filePath,
158: offset);
159:
160: char ch1 = filePath.charAt(offset + 1);
161: char ch2 = filePath.charAt(offset);
162:
163: if ((ch2 == '/' || ch2 == _separatorChar)
164: && (ch1 == '/' || ch1 == _separatorChar))
165: return super .schemeWalk(userPath, attributes,
166: convertFromWindowsPath(filePath.substring(offset)),
167: 0);
168: else
169: return super .schemeWalk(userPath, attributes, filePath,
170: offset);
171: }
172:
173: /**
174: * Lookup the actual path relative to the filesystem root.
175: *
176: * @param userPath the user's path to lookup()
177: * @param attributes the user's attributes to lookup()
178: * @param path the normalized path
179: *
180: * @return the selected path
181: */
182: public Path fsWalk(String userPath, Map<String, Object> attributes,
183: String path) {
184: return new FilePath(_root, userPath, path);
185: }
186:
187: /**
188: * Returns true if the path itself is cacheable
189: */
190: @Override
191: protected boolean isPathCacheable() {
192: return true;
193: }
194:
195: public String getScheme() {
196: return "file";
197: }
198:
199: /**
200: * Returns the full url for the given path.
201: */
202: public String getURL() {
203: if (!isWindows())
204: return escapeURL("file:" + getFullPath());
205:
206: String path = getFullPath();
207: int length = path.length();
208: CharBuffer cb = new CharBuffer();
209: cb.append("file:/");
210:
211: char ch;
212: int offset = 0;
213: // For windows, convert /c: to c:
214: if (length >= 3
215: && path.charAt(0) == '/'
216: && path.charAt(2) == ':'
217: && ('a' <= (ch = path.charAt(1)) && ch <= 'z' || 'A' <= ch
218: && ch <= 'Z')) {
219: offset = 1;
220: } else if (length >= 3 && path.charAt(0) == '/'
221: && path.charAt(1) == ':' && path.charAt(2) == '/') {
222: cb.append('/');
223: cb.append('/');
224: cb.append('/');
225: offset = 3;
226: }
227:
228: for (; offset < length; offset++) {
229: ch = path.charAt(offset);
230:
231: if (ch == '\\')
232: cb.append('/');
233: else
234: cb.append(ch);
235: }
236:
237: return escapeURL(cb.toString());
238:
239: }
240:
241: /**
242: * Returns the native path.
243: */
244: public String getNativePath() {
245: if (!isWindows())
246: return getFullPath();
247:
248: String path = getFullPath();
249: int length = path.length();
250: CharBuffer cb = new CharBuffer();
251: char ch;
252: int offset = 0;
253: // For windows, convert /c: to c:
254: if (isWindows()) {
255: if (length >= 3
256: && path.charAt(0) == '/'
257: && path.charAt(2) == ':'
258: && ('a' <= (ch = path.charAt(1)) && ch <= 'z' || 'A' <= ch
259: && ch <= 'Z')) {
260: offset = 1;
261: } else if (length >= 3 && path.charAt(0) == '/'
262: && path.charAt(1) == ':' && path.charAt(2) == '/') {
263: cb.append('\\');
264: cb.append('\\');
265: offset = 3;
266: }
267: }
268:
269: for (; offset < length; offset++) {
270: ch = path.charAt(offset);
271: if (ch == '/')
272: cb.append(_separatorChar);
273: else
274: cb.append(ch);
275: }
276:
277: return cb.close();
278: }
279:
280: public boolean exists() {
281: if (_separatorChar == '\\' && isAux())
282: return false;
283: else
284: return getFile().exists();
285: }
286:
287: public int getMode() {
288: int perms = 0;
289:
290: if (isDirectory()) {
291: perms += 01000;
292: perms += 0111;
293: }
294:
295: if (canRead())
296: perms += 0444;
297:
298: if (canWrite())
299: perms += 0222;
300:
301: return perms;
302: }
303:
304: public boolean isDirectory() {
305: return getFile().isDirectory();
306: }
307:
308: public boolean isFile() {
309: if (_separatorChar == '\\' && isAux())
310: return false;
311: else
312: return getFile().isFile();
313: }
314:
315: public long getLength() {
316: return getFile().length();
317: }
318:
319: public long getLastModified() {
320: return getFile().lastModified();
321: }
322:
323: // This exists in JDK 1.2
324: public void setLastModified(long time) {
325: getFile().setLastModified(time);
326: }
327:
328: public boolean canRead() {
329: File file = getFile();
330:
331: if (_separatorChar == '\\' && isAux())
332: return false;
333: else
334: return file.canRead();
335: }
336:
337: public boolean canWrite() {
338: File file = getFile();
339:
340: if (_separatorChar == '\\' && isAux())
341: return false;
342: else
343: return file.canWrite();
344: }
345:
346: /**
347: * Returns a list of files in the directory.
348: */
349: public String[] list() throws IOException {
350: String[] list = getFile().list();
351:
352: if (list != null)
353: return list;
354:
355: return new String[0];
356: }
357:
358: public boolean mkdir() throws IOException {
359: boolean value = getFile().mkdir();
360: if (!value && !getFile().isDirectory())
361: throw new IOException("cannot create directory");
362:
363: return value;
364: }
365:
366: public boolean mkdirs() throws IOException {
367: File file = getFile();
368:
369: boolean value;
370:
371: synchronized (file) {
372: value = file.mkdirs();
373: }
374:
375: if (!value && !file.isDirectory())
376: throw new IOException("Cannot create directory: "
377: + getFile());
378:
379: return value;
380: }
381:
382: public boolean remove() {
383: if (getFile().delete())
384: return true;
385:
386: if (getPath().endsWith(".jar")) {
387: // XXX:
388: // Jar.create(this).clearCache();
389: return getFile().delete();
390: }
391:
392: return false;
393: }
394:
395: @Override
396: public boolean truncate(long length) throws IOException {
397: File file = getFile();
398:
399: FileOutputStream fos = new FileOutputStream(file);
400:
401: try {
402: fos.getChannel().truncate(length);
403:
404: return true;
405: } finally {
406: fos.close();
407: }
408: }
409:
410: public boolean renameTo(Path path) {
411: if (!(path instanceof FilePath))
412: return false;
413:
414: FilePath file = (FilePath) path;
415:
416: return this .getFile().renameTo(file.getFile());
417: }
418:
419: /**
420: * Returns the stream implementation for a read stream.
421: */
422: public StreamImpl openReadImpl() throws IOException {
423: if (_separatorChar == '\\' && isAux())
424: throw new FileNotFoundException(_file.toString());
425:
426: /* XXX: only for Solaris (?)
427: if (isDirectory())
428: throw new IOException("is directory");
429: */
430:
431: return new FileReadStream(new FileInputStream(getFile()), this );
432: }
433:
434: public StreamImpl openWriteImpl() throws IOException {
435: FileWriteStream fws = new FileWriteStream(new FileOutputStream(
436: getFile()), this );
437:
438: fws.setNewline(NEWLINE);
439:
440: return fws;
441: }
442:
443: public StreamImpl openAppendImpl() throws IOException {
444: FileOutputStream fos = null;
445:
446: try {
447: fos = new FileOutputStream(getFile().toString(), true);
448: } catch (IOException e) {
449: // MacOS hack
450: fos = new FileOutputStream(getFile().toString());
451: }
452:
453: FileWriteStream fws = new FileWriteStream(fos);
454:
455: fws.setNewline(NEWLINE);
456:
457: return fws;
458: }
459:
460: public StreamImpl openReadWriteImpl() throws IOException {
461: VfsStream os;
462:
463: os = new VfsStream(new FileInputStream(getFile()),
464: new FileOutputStream(getFile()), this );
465:
466: os.setNewline(NEWLINE);
467:
468: return os;
469: }
470:
471: /**
472: * Returns the stream implementation for a random-access stream.
473: */
474: public RandomAccessStream openRandomAccess() throws IOException {
475: if (_separatorChar == '\\' && isAux())
476: throw new FileNotFoundException(_file.toString());
477:
478: return new FileRandomAccessStream(new RandomAccessFile(
479: getFile(), "rw"));
480: }
481:
482: @Override
483: protected Path copy() {
484: return new FilePath(getRoot(), getUserPath(), getPath());
485: }
486:
487: public int hashCode() {
488: return getPath().hashCode();
489: }
490:
491: public boolean equals(Object b) {
492: if (this == b)
493: return true;
494:
495: if (!(b instanceof FilePath))
496: return false;
497:
498: FilePath file = (FilePath) b;
499:
500: return getPath().equals(file.getPath());
501: }
502:
503: /**
504: * Lazily returns the native File object.
505: */
506: public File getFile() {
507: if (_file != null)
508: return _file;
509:
510: if (com.caucho.util.Alarm.isTest())
511: _file = new File(getFullPath());
512: else
513: _file = new File(getNativePath());
514:
515: return _file;
516: }
517:
518: /**
519: * Special case for the evil windows special
520: */
521: protected boolean isAux() {
522: File file = getFile();
523:
524: String path = getFullPath().toLowerCase();
525:
526: int len = path.length();
527: int p = path.indexOf("/aux");
528: int ch;
529: if (p >= 0 && (p + 4 >= len || path.charAt(p + 4) == '.'))
530: return true;
531:
532: p = path.indexOf("/con");
533: if (p >= 0 && (p + 4 >= len || path.charAt(p + 4) == '.'))
534: return true;
535:
536: p = path.indexOf("/nul");
537: if (p >= 0 && (p + 4 >= len || path.charAt(p + 4) == '.'))
538: return true;
539:
540: return false;
541: }
542: }
|