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.util.Map;
035:
036: /**
037: * Abstract FilesystemPath, the parent of hierarchical Paths like
038: * FilePath or HttpPath.
039: */
040: abstract public class FilesystemPath extends Path {
041: protected FilesystemPath _root;
042: protected BindPath _bindRoot;
043: protected String _pathname;
044: protected String _userPath;
045:
046: /**
047: * Create a new filesystemPath
048: *
049: * @param root Root of url space
050: * @param userPath the user's path
051: * @param pathname Canonical path
052: */
053: protected FilesystemPath(FilesystemPath root, String userPath,
054: String pathname) {
055: super (root);
056:
057: if (pathname == null)
058: throw new NullPointerException();
059:
060: _pathname = pathname;
061: _userPath = userPath;
062:
063: if (root != null) {
064: _root = root;
065: _bindRoot = root._bindRoot;
066: }
067: }
068:
069: /**
070: * Return the parent Path
071: */
072: public Path getParent() {
073: if (_pathname.length() <= 1)
074: return fsWalk("/", null, "/");
075:
076: int length = _pathname.length();
077: int lastSlash = _pathname.lastIndexOf('/');
078:
079: if (lastSlash < 1)
080: return fsWalk("/", null, "/");
081:
082: if (lastSlash == length - 1) {
083: lastSlash = _pathname.lastIndexOf('/', length - 2);
084: if (lastSlash < 1)
085: return fsWalk("/", null, "/");
086: }
087:
088: return fsWalk("..", null, _pathname.substring(0, lastSlash));
089: }
090:
091: /**
092: * schemeWalk is called by Path for a scheme lookup like file:/tmp/foo
093: *
094: * @param userPath the user's lookup() path
095: * @param attributes the user's attributes
096: * @param filePath the actual lookup() path
097: * @param offset offset into filePath
098: */
099: protected Path schemeWalk(String userPath,
100: Map<String, Object> attributes, String filePath, int offset) {
101: String canonicalPath;
102:
103: if (filePath.length() > offset
104: && (filePath.charAt(offset) == '/' || filePath
105: .charAt(offset) == _separatorChar))
106: canonicalPath = normalizePath("/", filePath, offset,
107: _separatorChar);
108: else
109: canonicalPath = normalizePath(_pathname, filePath, offset,
110: _separatorChar);
111:
112: return fsWalk(userPath, attributes, canonicalPath);
113: }
114:
115: /**
116: * Lookup a path relative to the current filesystem's root.
117: * Filesystems will specialize fsWalk.
118: *
119: * @param userPath the exact string passed by the user's lookup()
120: * @param newAttributes the user's new attributes
121: * @param newPath the normalized real path
122: *
123: * @return the matching path
124: */
125: abstract public Path fsWalk(String userPath,
126: Map<String, Object> newAttributes, String newPath);
127:
128: /**
129: * wrapper for the real normalize path routine to use CharBuffer.
130: *
131: * @param oldPath The parent Path's path
132: * @param newPath The user's new path
133: * @param offset Offset into the user path
134: *
135: * @return the normalized path
136: */
137: static protected String normalizePath(String oldPath,
138: String newPath, int offset, char separatorChar) {
139: CharBuffer cb = new CharBuffer();
140: normalizePath(cb, oldPath, newPath, offset, separatorChar);
141: return cb.toString();
142: }
143:
144: /**
145: * Normalizes a filesystemPath path.
146: *
147: * <ul>
148: * <li>foo//bar -> foo/bar
149: * <li>foo/./bar -> foo/bar
150: * <li>foo/../bar -> bar
151: * <li>/../bar -> /bar
152: * </ul>
153: *
154: * @param cb charBuffer holding the normalized result
155: * @param oldPath the parent path
156: * @param newPath the relative path
157: * @param offset where in the child path to start
158: */
159: static protected void normalizePath(CharBuffer cb, String oldPath,
160: String newPath, int offset, char separatorChar) {
161: cb.clear();
162: cb.append(oldPath);
163: if (cb.length() == 0 || cb.getLastChar() != '/')
164: cb.append('/');
165:
166: int length = newPath.length();
167: int i = offset;
168: while (i < length) {
169: char ch = newPath.charAt(i);
170: char ch2;
171:
172: switch (ch) {
173: default:
174: if (ch != separatorChar) {
175: cb.append(ch);
176: i++;
177: break;
178: }
179: // the separator character falls through to be treated as '/'
180:
181: case '/':
182: // "//" -> "/"
183: if (cb.getLastChar() != '/')
184: cb.append('/');
185: i++;
186: break;
187:
188: case '.':
189: if (cb.getLastChar() != '/') {
190: cb.append('.');
191: i++;
192: break;
193: }
194:
195: // "/." -> ""
196: if (i + 1 >= length) {
197: i += 2;
198: break;
199: }
200:
201: switch (newPath.charAt(i + 1)) {
202: default:
203: if (newPath.charAt(i + 1) != separatorChar) {
204: cb.append('.');
205: i++;
206: break;
207: }
208: // the separator falls through to be treated as '/'
209:
210: // "/./" -> "/"
211: case '/':
212: i += 2;
213: break;
214:
215: // "foo/.." -> ""
216: case '.':
217: if ((i + 2 >= length
218: || (ch2 = newPath.charAt(i + 2)) == '/' || ch2 == separatorChar)
219: && cb.getLastChar() == '/') {
220: int segment = cb.lastIndexOf('/',
221: cb.length() - 2);
222: if (segment == -1) {
223: cb.clear();
224: cb.append('/');
225: } else
226: cb.setLength(segment + 1);
227:
228: i += 3;
229: } else {
230: cb.append('.');
231: i++;
232: }
233: break;
234: }
235: }
236:
237: }
238:
239: // strip trailing "/"
240: /*
241: if (cb.length() > 1 && cb.getLastChar() == '/')
242: cb.setLength(cb.length() - 1);
243: */
244: }
245:
246: /**
247: * Returns the root.
248: */
249: public FilesystemPath getRoot() {
250: return _root;
251: }
252:
253: /**
254: * Returns the path portion of the URL.
255: */
256: public String getPath() {
257: return _pathname;
258: }
259:
260: /**
261: * Return's the application's name for the path, e.g. for
262: * a relative path.
263: */
264: public String getUserPath() {
265: return _userPath != null ? _userPath : _pathname;
266: }
267:
268: public void setUserPath(String path) {
269: _userPath = path;
270: }
271:
272: /**
273: * For chrooted filesystems return the real system path.
274: */
275: public String getFullPath() {
276: if (_root == this )
277: return getPath();
278:
279: String rootPath = _root.getFullPath();
280: String path = getPath();
281:
282: if (rootPath.length() <= 1)
283: return path;
284: else if (path.length() <= 1)
285: return rootPath;
286: else
287: return rootPath + path;
288: }
289:
290: public String getTail() {
291: String path = getPath();
292:
293: int length = path.length();
294: int p = path.lastIndexOf('/');
295: if (p == -1)
296: return "";
297: else if (p < length - 1)
298: return path.substring(p + 1);
299: else {
300: p = path.lastIndexOf('/', length - 2);
301: if (p < 0)
302: return "";
303: return path.substring(p + 1, length - 1);
304: }
305: }
306:
307: /**
308: * Essentially chroot
309: */
310: public Path createRoot(SchemeMap schemeMap) {
311: FilesystemPath restriction = (FilesystemPath) copy();
312:
313: restriction._schemeMap = schemeMap;
314: restriction._root = this ;
315: restriction._pathname = "/";
316: restriction._userPath = "/";
317:
318: return restriction;
319: }
320:
321: public void bind(Path context) {
322: if (_bindRoot == null)
323: _bindRoot = _root._bindRoot;
324:
325: if (_bindRoot == null) {
326: _bindRoot = new BindPath(_root);
327: _root._bindRoot = _bindRoot;
328: }
329:
330: _bindRoot.bind(getPath(), context);
331: }
332:
333: public int hashCode() {
334: return getURL().hashCode();
335: }
336:
337: public boolean equals(Object b) {
338: if (this == b)
339: return true;
340: else if (b == null || !getClass().equals(b.getClass()))
341: return false;
342:
343: Path bPath = (Path) b;
344:
345: return getURL().equals(bPath.getURL());
346: }
347: }
|