001: /*=============================================================================
002: * Copyright Texas Instruments 2003. All Rights Reserved.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License as published by the Free Software Foundation; either
007: * version 2 of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
012: * Lesser General Public License for more details.
013: *
014: * You should have received a copy of the GNU Lesser General Public
015: * License along with this library; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
017: */
018:
019: package oscript.fs;
020:
021: import java.io.*;
022: import java.util.*;
023:
024: import oscript.exceptions.ProgrammingErrorException;
025:
026: /**
027: * Abstract implementation of {@link AbstractFile}... hmm, the naming scheme
028: * isn't working out so well here, because AbstractFile. isn't a good name
029: * for an interface.
030: *
031: * @author Rob Clark (rob@ti.com)
032: */
033: public abstract class AbstractAbstractFile implements AbstractFile,
034: Serializable {
035: private String ext;
036: private String path;
037:
038: private static TempFileSystem tfs = new TempFileSystem();
039:
040: static {
041: AbstractFileSystem.mount(tfs, tfs.getMountPath());
042: }
043:
044: /**
045: * Class Constructor. If this file is created as a temporary file (ie.
046: * <code>temp</code> is <code>true</code>), then the <code>path</code>
047: * is interpreted as a relative path, or filename with no path prefix,
048: * and the file is created with a unique directory path, and the file
049: * will not {@link #equals} any other file object.
050: *
051: * @param path the file name
052: * @param temp is this a unique temporary file
053: */
054: public AbstractAbstractFile(String path, boolean temp) {
055: String subpath = null;
056:
057: if (temp) {
058: subpath = Math.random() + "/" + path;
059: path = tfs.getMountPath() + "/" + subpath;
060: }
061:
062: path = AbstractFileSystem.normalize(path);
063:
064: this .path = path;
065:
066: int idx = path.lastIndexOf('.');
067: if (idx != -1)
068: ext = path.substring(idx + 1);
069: else
070: ext = "";
071:
072: /* note that only temp files get registered, because tfs only handles
073: * things under /tmp... if tfs handled the entire filesystem space,
074: * then it would need to be involved in any sort of resolve/children
075: * operation, which for now I am staying away from for performance
076: * reasons... plus at this point non-temp AbstractAbstractFile's are
077: * a very special case.
078: */
079: if (temp)
080: tfs.registerFile(subpath, this );
081: }
082:
083: /**
084: * Is it possible to write to this file.
085: */
086: public boolean canWrite() {
087: return false;
088: }
089:
090: /**
091: * Is it possible to read from this file.
092: */
093: public boolean canRead() {
094: return true;
095: }
096:
097: /**
098: * Tests whether the file denoted by this abstract pathname exists.
099: *
100: * @return <code>true</code> iff the file exists
101: */
102: public boolean exists() {
103: return true;
104: }
105:
106: /**
107: * Test whether this file is a directory.
108: *
109: * @return <code>true</code> iff this file is a directory
110: */
111: public boolean isDirectory() {
112: return false;
113: }
114:
115: /**
116: * Test whether this file is a regular file. A file is a regular file if
117: * it is not a directory.
118: *
119: * @return <code>true</code> iff this file is a regular file.
120: */
121: public boolean isFile() {
122: return true;
123: }
124:
125: /**
126: * Return the time of last modification. The meaning of these value is not
127: * so important, but the implementation must ensure that a higher value is
128: * returned for the more recent version of a given element. Ie. if at some
129: * point this returns X, an <code>AbstractFile</code> representing the same
130: * "file", but created at a later time, should return X if the file has not
131: * been modified, or >X if the file has been modified.
132: *
133: * @return larger value indicates more recent modification
134: */
135: public long lastModified() {
136: return 0; // prehaps construction time?
137: }
138:
139: /**
140: * should be overriden to not provide a bogus answer
141: */
142: public long length() {
143: return -1;
144: }
145:
146: /**
147: * Create a new empty file, if it does not yet exist.
148: *
149: * @return <code>true</code> iff the file does not exist and was successfully
150: * created.
151: * @throws IOException if error
152: */
153: public boolean createNewFile() throws IOException {
154: throw new IOException("unsupported");
155: }
156:
157: /**
158: * Update the timestamp on this file to the current time.
159: *
160: * @throws IOException if error
161: */
162: public void touch() throws IOException {
163: throw new IOException("unsupported");
164: }
165:
166: /**
167: * Delete this file. If this file is a directory, then the directory must
168: * be empty.
169: *
170: * @return <code>true<code> iff the directory is successfully deleted.
171: * @throws IOException if error
172: */
173: public boolean delete() throws IOException {
174: throw new IOException("unsupported");
175: }
176:
177: /**
178: * If this file does not exist, create it as a directory.
179: *
180: * @return <code>true</code> iff directory successfully created
181: */
182: public boolean mkdir() throws IOException {
183: throw new IOException("unsupported");
184: }
185:
186: /**
187: * If this file does not exist, create it as a directory. All necessary
188: * parent directories are also created. If this operation fails, it may
189: * have successfully created some or all of the parent directories.
190: *
191: * @return <code>true</code> iff directory successfully created
192: */
193: public boolean mkdirs() throws IOException {
194: throw new IOException("unsupported");
195: }
196:
197: /**
198: * Get an output stream to write to this file.
199: *
200: * @return output stream
201: * @throws IOException if <code>canWrite</code> returns <code>false</code>
202: * @see #canWrite
203: */
204: public OutputStream getOutputStream(boolean append)
205: throws IOException {
206: throw new IOException("unsupported");
207: }
208:
209: /**
210: * Get the extension, which indicates the type of file. Usually the extension
211: * is part of the filename, ie. if the extension was <i>os</i>, the filename
212: * would end with <i>.os</i>.
213: *
214: * @return a string indicating the type of file
215: */
216: public String getExtension() {
217: return ext;
218: }
219:
220: /**
221: * Get the file path, which globally identifies the file.
222: *
223: * @return a unique string
224: * @see #getName
225: */
226: public String getPath() {
227: return path;
228: }
229:
230: /**
231: * Get the name of this file, which is the last component of the complete path.
232: *
233: * @return the file name
234: * @see #getPath
235: */
236: public String getName() {
237: int idx = path.lastIndexOf("/");
238: if (idx != -1)
239: return path.substring(idx + 1);
240: else
241: return path;
242: }
243:
244: public String toString() {
245: return getPath();
246: }
247:
248: public int hashCode() {
249: return getPath().hashCode();
250: }
251:
252: public boolean equals(Object obj) {
253: return (obj instanceof AbstractFile)
254: && ((AbstractFile) obj).getPath().equals(getPath());
255: }
256: }
257:
258: /**
259: * The filesystem which enables temp files to appear in the filesystem. It
260: * holds weak-references to the temp files that get created, in order to
261: * track what temp files are currently live.
262: */
263: class TempFileSystem extends AbstractFileSystem {
264: static java.lang.ref.ReferenceQueue rq = new java.lang.ref.ReferenceQueue();
265:
266: /**
267: * table of files currently registered with the tmp filesystem
268: */
269: private Hashtable fileTable = new Hashtable();
270:
271: /**
272: * Class constructor.
273: */
274: TempFileSystem() {
275: oscript.util.WorkerThread.addRunnable(new Runnable() {
276: public void run() {
277: AbstractFileReference afr;
278: while ((afr = (AbstractFileReference) (rq.poll())) != null)
279: afr.cleanup();
280: }
281: }, 500);
282: }
283:
284: /**
285: * Get the path to where this filesystem should be mounted.
286: */
287: String getMountPath() {
288: return "/.tmp";
289: }
290:
291: /**
292: * Register a file that is created within this filesystem's space
293: */
294: AbstractFileReference registerFile(String subpath, AbstractFile file) {
295: synchronized (fileTable) {
296: AbstractFileReference afr = new AbstractFileReference(
297: subpath, file);
298: fileTable.put(subpath, afr);
299: return afr;
300: }
301: }
302:
303: /**
304: * A weak-reference for tracking the file.
305: */
306: class AbstractFileReference extends java.lang.ref.WeakReference {
307: private String subpath;
308: private AbstractFile parent;
309: private AbstractFileReference parentRef;
310:
311: AbstractFileReference(String subpath, AbstractFile file) {
312: super (file, rq);
313: this .subpath = subpath;
314:
315: int idx = subpath.lastIndexOf('/');
316: if (idx != -1) {
317: String parentSubpath = subpath.substring(0, idx);
318: parent = new BogusFile(getMountPath() + "/"
319: + parentSubpath, true, true, false);
320: parentRef = registerFile(parentSubpath, parent);
321: }
322: }
323:
324: String getSubpath() {
325: return subpath;
326: }
327:
328: AbstractFile getFile() {
329: return (AbstractFile) get();
330: }
331:
332: void cleanup() {
333: synchronized (fileTable) {
334: if (parentRef != null)
335: parentRef.cleanup();
336: fileTable.remove(getSubpath());
337: }
338: }
339: }
340:
341: /**
342: * Try to resolve the specified path. If unresolved, return <code>null</code>.
343: *
344: * @param mountPath the path this fs is mounted at to resolve the requested file
345: * @param subpath path to file, relative to <code>mountPath</code>
346: * @return file or <code>null</code>
347: */
348: protected AbstractFile resolveInFileSystem(String mountPath,
349: String subpath) throws IOException {
350: if (!mountPath.equals(getMountPath()))
351: throw new ProgrammingErrorException(
352: "this shouldn't happen! send this error msg to rob@ti.com");
353:
354: synchronized (fileTable) {
355: AbstractFileReference afr = (AbstractFileReference) (fileTable
356: .get(subpath));
357:
358: if (afr != null)
359: return afr.getFile();
360: }
361:
362: return null;
363: }
364:
365: /**
366: * Return an iterator of children of the specified path.
367: *
368: * @param mountPath the path this fs is mounted at to resolve the requested file
369: * @param subpath path to file, relative to <code>mountPath</code>
370: * @return an iterator of <code>AbstractFile</code>
371: */
372: protected Collection childrenInFileSystem(String mountPath,
373: String subpath) throws IOException {
374: LinkedList fileList = new LinkedList();
375:
376: synchronized (fileTable) {
377: for (Iterator itr = fileTable.values().iterator(); itr
378: .hasNext();) {
379: AbstractFileReference afr = (AbstractFileReference) (itr
380: .next());
381: String afrpath = afr.getSubpath();
382: // XXX this assumes the paths are properly normalized...
383: if (afrpath.startsWith(subpath)
384: && (afrpath.length() > subpath.length())
385: && (afrpath.indexOf('/', subpath.length() + 1) == -1)) {
386: AbstractFile file = afr.getFile();
387: if (file != null)
388: fileList.add(file);
389: }
390: }
391: }
392:
393: return fileList;
394: }
395:
396: /**
397: * Flush any pending changes within this filesystem.
398: */
399: protected void flush() throws IOException {
400: /* no-op */
401: }
402: }
403:
404: /*
405: * Local Variables:
406: * tab-width: 2
407: * indent-tabs-mode: nil
408: * mode: java
409: * c-indentation-style: java
410: * c-basic-offset: 2
411: * eval: (c-set-offset 'substatement-open '0)
412: * eval: (c-set-offset 'case-label '+)
413: * eval: (c-set-offset 'inclass '+)
414: * eval: (c-set-offset 'inline-open '0)
415: * End:
416: */
|