--- /dev/null
+// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
+package org.ibex.js;
+
+import org.ibex.util.*;
+import java.util.*;
+import java.io.*;
+
+// FEATURE: support for move
+// FEATURE: support for bytestreams
+// FEATURE: cache directories so we can do equality checking on them?
+// FEATURE: autoconvert "true" to true and "0.3" to 0.3 on readback
+
+/**
+ * A crude mechanism for using a filesystem as object storage.
+ *
+ * This object represents a directory; writing a string, number, or
+ * boolean to any of its properties will create a file with the
+ * (encoded) property name as its filename and the "stringified"
+ * value as its contents.
+ *
+ * Writing 'null' to one of this object's properties will
+ * [recursively if necessary] delete the corresponding directory
+ * entry.
+ *
+ * Writing any other object to one of this object's properties will
+ * create a new Directory object and copy the other object's keys()
+ * into the new Directory. This means that assigning one directory
+ * to a property of another directory will <i>copy</i> the directory,
+ * not move it. There is currently no way to move directories.
+ *
+ * If an object is written to a property that already has an entry,
+ * the old one is deleted (equivalent to writing 'null') first.
+ *
+ * WARNING: when instantiating a Directory object with a file
+ * argument that points to a non-directory File, this class will
+ * delete that file and create a directory!
+ */
+public class Directory extends JS {
+
+ File f;
+
+ /**
+ * Create the directory object. Existing directories will be
+ * preserved; if a file is present it will be obliterated.
+ */
+ public Directory(File f) throws IOException {
+ this.f = f;
+ if (!f.exists()) new Directory(new File(f.getParent()));
+ if (!f.isDirectory()) destroy(f);
+ f.mkdirs();
+ }
+
+ private static void destroy(File f) throws IOException {
+ if (!f.exists()) return;
+ if (f.isDirectory()) {
+ String[] entries = f.list();
+ for(int i=0; i<entries.length; i++) destroy(new File(f.getAbsolutePath() + File.separatorChar + entries[i]));
+ }
+ f.delete();
+ }
+
+ public void put(Object key0, Object val) throws JSExn {
+ try {
+ if (key0 == null) return;
+ String key = toString(key0);
+ File f2 = new File(f.getAbsolutePath() + File.separatorChar + FileNameEncoder.encode(key));
+ destroy(f2);
+ if (val == null) return;
+ if (val instanceof JS) {
+ Directory d2 = new Directory(f2);
+ Enumeration e = ((JS)val).keys();
+ while(e.hasMoreElements()) {
+ String k = (String)e.nextElement();
+ Object v = ((JS)val).get(k);
+ d2.put(k, v);
+ }
+ } else {
+ OutputStream out = new FileOutputStream(f2);
+ Writer w = new OutputStreamWriter(out);
+ w.write(toString(val));
+ w.flush();
+ out.close();
+ }
+ } catch (IOException ioe) {
+ throw new JSExn.IO(ioe);
+ }
+ }
+
+ public Object get(Object key0) throws JSExn {
+ try {
+ if (key0 == null) return null;
+ String key = toString(key0);
+ File f2 = new File(f.getAbsolutePath() + File.separatorChar + FileNameEncoder.encode(key));
+ if (!f2.exists()) return null;
+ if (f2.isDirectory()) return new Directory(f2);
+ char[] chars = new char[(int)f2.length()];
+ int numchars = 0;
+ Reader r = new InputStreamReader(new FileInputStream(f2));
+ while(true) {
+ int numread = r.read(chars, numchars, chars.length - numchars);
+ if (numread == -1) return new String(chars, 0, numchars);
+ numchars += numread;
+ }
+ } catch (IOException ioe) {
+ throw new JSExn.IO(ioe);
+ }
+ }
+
+ public Enumeration keys() {
+ final String[] elements = f.list();
+ return new Enumeration() {
+ int i = 0;
+ public boolean hasMoreElements() { return i < elements.length; }
+ public Object nextElement() { return FileNameEncoder.decode(elements[i++]); }
+ };
+ }
+}