001: /*=============================================================================
002: * Copyright Texas Instruments 2000. 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: * $ProjectHeader: OSCRIPT 0.155 Fri, 20 Dec 2002 18:34:22 -0800 rclark $
019: */
020:
021: package oscript.fs;
022:
023: import java.io.*;
024: import java.util.*;
025:
026: /**
027: * An implementation of {@link oscript.AbstractFileSystem} for the local
028: * filesystem.
029: *
030: * @author Rob Clark (rob@ti.com)
031: * <!--$Format: " * @version $Revision$"$-->
032: * @version 1.15
033: */
034: public class LocalFileSystem extends AbstractFileSystem {
035: private File root;
036:
037: /**
038: * There are some hacks to make things work correctly under windows. I could
039: * do them all the time, but I am afraid of the possibility that they could
040: * break things on another OS, so we only enable them if we detect that the
041: * OS is windows. Called from OscriptInterpreter. Possibly I should
042: */
043: static {
044: String osName = System.getProperty("os.name");
045:
046: if (enableWindozeHacks) {
047: if (oscript.data.Value.DEBUG) {
048: System.err
049: .println("You appear to be using a Windows OS. Enabling LocalFileSystem hacks to");
050: System.err.println("work around Windows bugs");
051: }
052: final Vector drives = new java.util.Vector();
053: // install a background task to periodically check for newly mounted
054: // drives, and mount them into the AbstractFileSystem:
055: oscript.util.WorkerThread.addRunnable(new Runnable() {
056: public void run() {
057: try {
058: File[] roots = File.listRoots();
059: if (drives.size() == 0) {
060: for (int i = 0; i < roots.length; i++)
061: drives.add(roots[i]);
062: } else {
063: for (int i = 0; i < roots.length; i++) {
064: int found = drives.indexOf(roots[i]);
065: // a new drive is found
066: if (found == -1) {
067: AbstractFileSystem.mount(
068: new LocalFileSystem(
069: roots[i]), "/"
070: + roots[i]);
071: drives.add(roots[i]);
072: }
073: }//end of the for loop
074: }
075: } catch (java.util.ConcurrentModificationException e) {
076: // ignore
077: }
078: }
079: }, 250);
080: }
081: }
082:
083: /**
084: * Class Constructor.
085: *
086: * @param root the root of the local filesystem
087: */
088: public LocalFileSystem(File root) {
089: this .root = root;
090: }
091:
092: /**
093: * Class Constructor.
094: *
095: * @param root the root of the local filesystem
096: */
097: public LocalFileSystem(String root) {
098: this (new File(root));
099: }
100:
101: /**
102: * Try to resolve the specified path. If unresolved, return <code>null</code>.
103: *
104: * @param mountPath the path this fs is mounted at to resolve the requested file
105: * @param path path to file
106: * @return file or <code>null</code>
107: */
108: protected AbstractFile resolveInFileSystem(String mountPath,
109: String path) {
110: return makeLocalFile(mountPath, path);
111: }
112:
113: /**
114: * Return an iterator of children of the specified path.
115: *
116: * @param mountPath the path this fs is mounted at to resolve the requested file
117: * @param path path to file, relative to <code>mountPath</code>
118: * @return a collection of <code>AbstractFile</code>
119: */
120: protected Collection childrenInFileSystem(final String mountPath,
121: String path) {
122: final LocalFile lf = (LocalFile) (resolveInFileSystem(
123: mountPath, path));
124: final String prefix = path.equals("") ? path : (path + "/");
125:
126: return new AbstractCollection() {
127:
128: String[] names = lf.list();
129:
130: public int size() {
131: if (names == null)
132: return 0;
133: return names.length;
134: }
135:
136: public Iterator iterator() {
137: return new Iterator() {
138:
139: int idx = 0;
140:
141: public boolean hasNext() {
142: return (names != null) && (idx < names.length);
143: }
144:
145: public Object next() {
146: if (!hasNext())
147: throw new java.util.NoSuchElementException();
148: return makeLocalFile(mountPath, prefix
149: + names[idx++]);
150: }
151:
152: public void remove() {
153: throw new UnsupportedOperationException(
154: "remove");
155: }
156:
157: };
158: }
159: };
160: }
161:
162: /**
163: * Flush any pending changes within this filesystem.
164: */
165: protected void flush() throws IOException {
166: // no-op
167: }
168:
169: /**
170: * Call this to construct a LocalFile, rather than calling the constructor
171: * directly. This will ensure that the correct sort of LocalFile gets
172: * created.
173: */
174: private final LocalFile makeLocalFile(String mountPath, String path) {
175: if (enableWindozeHacks)
176: return new WindozeLocalFile(mountPath, path);
177: else
178: return new LocalFile(mountPath, path);
179: }
180:
181: /**
182: * Implementation of abstract-file for the local filesystem.
183: */
184: class LocalFile extends File implements AbstractFile {
185: private String path;
186: private String vpath; // path within virtual filesystem
187: private String ext;
188:
189: /**
190: * Class Constructor.
191: */
192: LocalFile(String mountPath, String path) {
193: super (root, path);
194:
195: this .vpath = path.length() == 0 ? mountPath : mountPath
196: + "/" + path;
197: this .path = path;
198: //System.err.println("root=" + root.getAbsolutePath() + ", path=" + path + ", vpath=" + vpath + "\n -> " + getAbsolutePath());
199:
200: int idx = path.lastIndexOf('.');
201: if (idx != -1)
202: ext = path.substring(idx + 1);
203: else
204: ext = "";
205: }
206:
207: public boolean exists() {
208: if (path.length() > 0)
209: return super .exists();
210: return false;
211: }
212:
213: private String getActualPath() {
214: return root.getAbsolutePath() + '/' + path;
215: }
216:
217: /**
218: * Get an input stream to read from this file.
219: *
220: * @return input stream
221: * @throws IOException if <code>canRead</code> returns <code>true</code>
222: * @see #canRead
223: */
224: public InputStream getInputStream() throws IOException {
225: if (isDirectory())
226: throw new IOException("cannot read directory: " + this );
227:
228: return new FileInputStream(getActualPath());
229: }
230:
231: /**
232: * Get an output stream to write to this file.
233: *
234: * @return output stream
235: * @throws IOException if <code>canWrite</code> returns <code>false</code>
236: * @see #canWrite
237: */
238: public OutputStream getOutputStream(boolean append)
239: throws IOException {
240: if (isDirectory())
241: throw new IOException("cannot write directory: " + this );
242:
243: return new FileOutputStream(getActualPath(), append);
244: }
245:
246: /**
247: * Create a new empty file, if it does not yet exist.
248: *
249: * @return <code>true</code> iff the file does not exist and was successfully
250: * created.
251: * @throws IOException if error
252: */
253: public boolean createNewFile() throws IOException {
254: File parent = getParentFile();
255: createParent(parent);
256: if (path.indexOf('/') == -1)
257: touchMountPoint(LocalFileSystem.this );
258: return super .createNewFile();
259: }
260:
261: /**
262: * Update the timestamp on this file to the current time.
263: *
264: * @throws IOException if error
265: */
266: public void touch() throws IOException {
267: setLastModified(System.currentTimeMillis());
268: }
269:
270: /**
271: * Get the extension, which indicates the type of file. Usually the extension
272: * is part of the filename, ie. if the extension was <i>os</i>, the filename
273: * would end with <i>.os</i>.
274: *
275: * @return a string indicating the type of file
276: */
277: public String getExtension() {
278: return ext;
279: }
280:
281: /**
282: * Get the file path, which globally identifies the file.
283: *
284: * @return a unique string
285: */
286: public String getPath() {
287: return vpath;
288: }
289: }
290:
291: /**
292: * The WindozeLocalFile overloads some methods to work around some windows OS
293: * bugs that have been observed. There are two problems that have been seen.
294: * The first is that <code>(new java.io.File("X:")).exists()</code> returns
295: * <code>true</code> for network drives, but <code>false</code> for local
296: * drives. The second is that in either case the <code>getName()</code>
297: * method returns the empty string.
298: */
299: class WindozeLocalFile extends LocalFile {
300: private boolean hide;
301:
302: WindozeLocalFile(String mountPath, String path) {
303: super (mountPath = mountPath.replace('/', '\\'), path = path
304: .replace('/', '\\'));
305:
306: hide = "".equals(path);
307: }
308:
309: public boolean exists() {
310: if (hide)
311: return false;
312: return super .exists();
313: }
314:
315: public String getPath() {
316: return normalize(super .getPath());
317: }
318: }
319:
320: private static void createParent(File parent) {
321: if (parent != null) {
322: createParent(parent.getParentFile());
323: if (!parent.exists())
324: parent.mkdir();
325: }
326: }
327: }
328:
329: /*
330: * Local Variables:
331: * tab-width: 2
332: * indent-tabs-mode: nil
333: * mode: java
334: * c-indentation-style: java
335: * c-basic-offset: 2
336: * eval: (c-set-offset 'substatement-open '0)
337: * eval: (c-set-offset 'case-label '+)
338: * eval: (c-set-offset 'inclass '+)
339: * eval: (c-set-offset 'inline-open '0)
340: * End:
341: */
|