update Directory, Fountain, JSReflection, SOAP, XMLRPC to use baskets and new JS...
[org.ibex.js.git] / src / org / ibex / js / Directory.java
1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package org.ibex.js; 
6
7 import org.ibex.util.*; 
8 import java.io.*;
9
10 // FEATURE: support for move
11 // FEATURE: support for bytestreams
12 // FEATURE: cache directories so we can do equality checking on them?
13 // FEATURE: autoconvert "true" to true and "0.3" to 0.3 on readback
14
15 /** 
16  * A crude mechanism for using a filesystem as object storage.
17  *
18  *  This object represents a directory; writing a string, number, or
19  *  boolean to any of its properties will create a file with the
20  *  (encoded) property name as its filename and the "stringified"
21  *  value as its contents.
22  *
23  *  Writing 'null' to one of this object's properties will
24  *  [recursively if necessary] delete the corresponding directory
25  *  entry.
26  *  
27  *  Writing any other object to one of this object's properties will
28  *  create a new Directory object and copy the other object's keys()
29  *  into the new Directory.  This means that assigning one directory
30  *  to a property of another directory will <i>copy</i> the directory,
31  *  not move it.  There is currently no way to move directories.
32  *
33  *  If an object is written to a property that already has an entry,
34  *  the old one is deleted (equivalent to writing 'null') first.
35  * 
36  *  WARNING: when instantiating a Directory object with a file
37  *  argument that points to a non-directory File, this class will
38  *  delete that file and create a directory!
39  */
40 public class Directory extends JS.Immutable {
41
42     File f;
43
44     /** 
45      *  Create the directory object.  Existing directories will be
46      *  preserved; if a file is present it will be obliterated.
47      */ 
48
49     public Directory(File f) throws IOException {
50         this.f = f;
51         if (!f.exists()) new Directory(new File(f.getParent()));
52         if (!f.isDirectory()) destroy(f);
53         f.mkdirs();
54     }
55
56     private static void destroy(File f) throws IOException {
57         if (!f.exists()) return;
58         if (f.isDirectory()) {
59             String[] entries = f.list();
60             for(int i=0; i<entries.length; i++) destroy(new File(f.getAbsolutePath() + File.separatorChar + entries[i]));
61         }
62         f.delete();
63     }
64
65     public void put(JS key0, JS val) throws JSExn {
66         try {
67             if (key0 == null) return;
68             String key = Script.toString(key0);
69             File f2 = new File(f.getAbsolutePath() + File.separatorChar + Encode.toFilename(key));
70             destroy(f2);
71             if (val == null) return;
72             if (val instanceof JSPrimitive) {
73                 OutputStream out = new FileOutputStream(f2);
74                 Writer w = new OutputStreamWriter(out);
75                 w.write(Script.toString(val));
76                 w.flush();
77                 out.close();
78             } else {
79                 Directory d2 = new Directory(f2);
80                 JS.Enumeration e = val.keys();
81                 while(e.hasNext()) {
82                     JS k = e.next();
83                     JS v = val.get(k);
84                     d2.put(k, v);
85                 }
86             }
87         } catch (IOException ioe) {
88             throw new JSExn.IO(ioe);
89         }
90     }
91
92     public JS get(JS key0) throws JSExn {
93         try {
94             if (key0 == null) return null;
95             String key = Script.toString(key0);
96             File f2 = new File(f.getAbsolutePath() + File.separatorChar + Encode.toFilename(key));
97             if (!f2.exists()) return null;
98             if (f2.isDirectory()) return new Directory(f2);
99             char[] chars = new char[((int)f2.length()) * 2];
100             int numchars = 0;
101             Reader r = new InputStreamReader(new FileInputStream(f2));
102             while(true) {
103                 int numread = r.read(chars, numchars, chars.length - numchars);
104                 if (numread == -1) return Script.S(new String(chars, 0, numchars));
105                 numchars += numread;
106             }
107         } catch (IOException ioe) {
108             throw new JSExn.IO(ioe);
109         }
110     }
111
112     public JS.Enumeration keys() {
113         final String[] elements = f.list();
114         return new JS.Enumeration(null) {
115                 int i = 0;
116                 public boolean _hasNext() { return i < elements.length; }
117                 public JS _next() { return Script.S(Encode.fromFilename(elements[i++])); }
118             };
119     }
120 }