stream cleanup
authorbrian <brian@brianweb.net>
Tue, 6 Jul 2004 12:14:00 +0000 (12:14 +0000)
committerbrian <brian@brianweb.net>
Tue, 6 Jul 2004 12:14:00 +0000 (12:14 +0000)
darcs-hash:20040706121400-24bed-142e9a11c29ccbf41e3e7fde786a4069ccd84e6c.gz

src/org/ibex/core/Ibex.java
src/org/ibex/core/Template.java
src/org/ibex/graphics/Font.java
src/org/ibex/graphics/Picture.java
src/org/ibex/js/JS.java
src/org/ibex/js/Stream.java

index 58969ae..66d9189 100644 (file)
@@ -15,11 +15,11 @@ public final class Ibex extends JS implements JS.Cloneable {
     // FIXME remove this
     private final JS rr;
 
     // FIXME remove this
     private final JS rr;
 
-    public Ibex(Stream rr) { try { this.rr = bless(rr);} catch(JSExn e) { throw new Error("should never happen"); } }
+    public Ibex(Stream rr) { try { this.rr = bless(rr);} catch(JSExn e) { throw new Error("should never happen: " + e); } }
 
     public JS resolveString(String str, boolean permitAbsolute) throws JSExn {
         if (str.indexOf("://") != -1) {
 
     public JS resolveString(String str, boolean permitAbsolute) throws JSExn {
         if (str.indexOf("://") != -1) {
-            if (permitAbsolute) return (Stream)url2res(str);
+            if (permitAbsolute) return url2res(str);
             throw new JSExn("absolute URL " + str + " not permitted here");
         }
         // root-relative
             throw new JSExn("absolute URL " + str + " not permitted here");
         }
         // root-relative
@@ -171,10 +171,10 @@ public final class Ibex extends JS implements JS.Cloneable {
                         return ((JS)a).jsclone();
                     case "bless": return bless((JS)a);
                     case "ui.browser": Platform.newBrowserWindow(JS.toString(a)); return null;
                         return ((JS)a).jsclone();
                     case "bless": return bless((JS)a);
                     case "ui.browser": Platform.newBrowserWindow(JS.toString(a)); return null;
-                    case "stream.unzip": return new Stream.Zip((Stream)a);
-                    case "stream.uncab": return new Stream.Cab((Stream)a);
+                    case "stream.unzip": return a == null ? null : new Stream.Zip(a);
+                    case "stream.uncab": return a == null ? null : new Stream.Cab(a);
                     case "stream.cache":
                     case "stream.cache":
-                        try { return new Stream.CachedStream((Stream)a, "resources", true); }
+                        try { return a == null ? null : new Stream.CachedStream(a, "resources", true); }
                         catch (Stream.NotCacheableException e) { throw new JSExn("this resource cannot be cached"); }
                     case "stream.url": {
                         String url = JS.toString(a);
                         catch (Stream.NotCacheableException e) { throw new JSExn("this resource cannot be cached"); }
                     case "stream.url": {
                         String url = JS.toString(a);
@@ -196,22 +196,23 @@ public final class Ibex extends JS implements JS.Cloneable {
                     case "crypto.sha1": /* FEATURE */ return null;
                     case "crypto.rc4": /* FEATURE */ return null;
                     case "stream.parse.html": throw new JSExn("not implemented yet"); //return null;
                     case "crypto.sha1": /* FEATURE */ return null;
                     case "crypto.rc4": /* FEATURE */ return null;
                     case "stream.parse.html": throw new JSExn("not implemented yet"); //return null;
-                    case "stream.parse.xml": new XMLHelper((JS)b).doParse((JS)a); return null;
+                    case "stream.parse.xml": if(a == null) return null; new XMLHelper(b).doParse(a); return null;
                         // FIXME backgrounding
                         // FIXME backgrounding
-                    case "stream.parse.utf8": try { return JS.S(new String(InputStreamToByteArray.convert(Stream.getInputStream(a)))); }
+                    case "stream.parse.utf8": if(a == null) return null;
+                                              try { return JS.S(new String(InputStreamToByteArray.convert(a.getInputStream()))); }
                                               catch (Exception e) { Log.warn(this, e); }
                     //#end
                     break;
                 case 2:
                     //#switch(name)
                                               catch (Exception e) { Log.warn(this, e); }
                     //#end
                     break;
                 case 2:
                     //#switch(name)
-                    case "stream.watch": return new Stream.ProgressWatcher((Stream)a, (JS)b);
+                    case "stream.watch": return new Stream.ProgressWatcher(a, b);
                     case "regexp": return new JSRegexp(a, b);
                     //#end
                 case 3:
                     //#switch(name)
                     case "regexp": return new JSRegexp(a, b);
                     //#end
                 case 3:
                     //#switch(name)
-                    case "ui.font.height": return N(Font.getFont((Stream)a, JS.toInt(b)).textheight(JS.toString(c)));
+                    case "ui.font.height": return N(Font.getFont(a, JS.toInt(b)).textheight(JS.toString(c)));
                     case "ui.font.wait": throw new Error("FIXME: ibex.ui.font.wait not implemented");
                     case "ui.font.wait": throw new Error("FIXME: ibex.ui.font.wait not implemented");
-                    case "ui.font.width": return N(Font.getFont((Stream)a, JS.toInt(b)).textwidth(JS.toString(c)));
+                    case "ui.font.width": return N(Font.getFont(a, JS.toInt(b)).textwidth(JS.toString(c)));
                     //#end
                     break;
             }
                     //#end
                     break;
             }
@@ -224,7 +225,7 @@ public final class Ibex extends JS implements JS.Cloneable {
         throw new JSExn("invalid number of arguments ("+nargs+") for ibex object method "+name+"()");
     }
 
         throw new JSExn("invalid number of arguments ("+nargs+") for ibex object method "+name+"()");
     }
 
-    public Stream url2res(String url) throws JSExn {
+    public JS url2res(String url) throws JSExn {
         if (url.startsWith("http://")) return new Stream.HTTP(url);
         else if (url.startsWith("https://")) return new Stream.HTTP(url);
         else if (url.startsWith("data:")) return new Stream.ByteArray(Base64.decode(url.substring(5)), null);
         if (url.startsWith("http://")) return new Stream.HTTP(url);
         else if (url.startsWith("https://")) return new Stream.HTTP(url);
         else if (url.startsWith("data:")) return new Stream.ByteArray(Base64.decode(url.substring(5)), null);
@@ -333,7 +334,7 @@ public final class Ibex extends JS implements JS.Cloneable {
 
         public void doParse(JS s) throws JSExn {
             try { 
 
         public void doParse(JS s) throws JSExn {
             try { 
-                parse(new BufferedReader(new InputStreamReader(Stream.getInputStream(s))));
+                parse(new BufferedReader(new InputStreamReader(s.getInputStream())));
             } catch (Wrapper e) {
                 throw e.wrapee;
             } catch (XML.Exn e) {
             } catch (Wrapper e) {
                 throw e.wrapee;
             } catch (XML.Exn e) {
@@ -370,15 +371,16 @@ public final class Ibex extends JS implements JS.Cloneable {
             if (!(js instanceof Blessing)) return null;
             return (Blessing)js;
         }
             if (!(js instanceof Blessing)) return null;
             return (Blessing)js;
         }
+        // FEATURE: This is a gross hack
         public InputStream getImage() throws JSExn {
             try {
         public InputStream getImage() throws JSExn {
             try {
-                InputStream in = Stream.getInputStream(this);
+                InputStream in = getInputStream();
                 if (in != null) return in;
             } catch (IOException e) { /* DELIBERATE */ }
             String[] exts = new String[] { ".png", ".jpeg", ".gif" };
             for (int i=0; i < exts.length; i++)
                 try {
                 if (in != null) return in;
             } catch (IOException e) { /* DELIBERATE */ }
             String[] exts = new String[] { ".png", ".jpeg", ".gif" };
             for (int i=0; i < exts.length; i++)
                 try {
-                    InputStream in = Stream.getInputStream(parent.get(JS.S(JS.toString(parentkey) + exts[i])));
+                    InputStream in = parent.get(JS.S(JS.toString(parentkey) + exts[i])).getInputStream();
                     if (in != null) return in;
                 } catch (IOException f) { /* DELIBERATE */ }
             return null;
                     if (in != null) return in;
                 } catch (IOException f) { /* DELIBERATE */ }
             return null;
@@ -387,9 +389,8 @@ public final class Ibex extends JS implements JS.Cloneable {
             try {
                 if (t == null) {
                     // FEATURE: Might want to handle the ".t" part better
             try {
                 if (t == null) {
                     // FEATURE: Might want to handle the ".t" part better
-                    JS res = (JS) parent.get(JS.S(JS.toString(parentkey) + ".t"));
-                    // FIXME: need a better description (Stream.toString())
-                    t = Template.buildTemplate(JS.toString(parentkey), res, ibex);
+                    JS res = parent.get(JS.S(JS.toString(parentkey) + ".t"));
+                    t = Template.buildTemplate(description(), res, ibex);
                 }
                 return t != null ? t.staticScope : null;
             } catch (Exception e) {
                 }
                 return t != null ? t.staticScope : null;
             } catch (Exception e) {
@@ -397,6 +398,11 @@ public final class Ibex extends JS implements JS.Cloneable {
                 return null;
             }
         }
                 return null;
             }
         }
+        private String description() {
+            String s = JS.debugToString(parentkey);
+            for(Blessing b = parent; b != null; b = b.parent) s = JS.debugToString(parentkey) + "." + s;
+            return s;
+        }
         public JS call(JS a, JS b, JS c, JS[] rest, int nargs) throws JSExn {
             if (nargs != 1) throw new JSExn("can only call a template with one arg");
             getStatic();
         public JS call(JS a, JS b, JS c, JS[] rest, int nargs) throws JSExn {
             if (nargs != 1) throw new JSExn("can only call a template with one arg");
             getStatic();
index 7f342dd..efa3bbb 100644 (file)
@@ -157,7 +157,7 @@ public class Template {
        public TemplateHelper(String sourceName, JS s, Ibex ibex) throws XML.Exn, IOException, JSExn {
             this.sourceName = sourceName;
             this.ibex = ibex;
        public TemplateHelper(String sourceName, JS s, Ibex ibex) throws XML.Exn, IOException, JSExn {
             this.sourceName = sourceName;
             this.ibex = ibex;
-            InputStream is = Stream.getInputStream(s);
+            InputStream is = s.getInputStream();
             Ibex.Blessing b = Ibex.Blessing.getBlessing(s).parent;
             while(b != null) {
                 if(b.parentkey != null) initial_uri = JS.toString(b.parentkey) + (initial_uri.equals("") ? "" : "." + initial_uri);
             Ibex.Blessing b = Ibex.Blessing.getBlessing(s).parent;
             while(b != null) {
                 if(b.parentkey != null) initial_uri = JS.toString(b.parentkey) + (initial_uri.equals("") ? "" : "." + initial_uri);
index 35e9cf2..96c65c4 100644 (file)
@@ -136,7 +136,7 @@ public class Font {
         
         try {
             Log.info(Font.class, "loading font " + JS.debugToString(res));
         
         try {
             Log.info(Font.class, "loading font " + JS.debugToString(res));
-            InputStream is = Stream.getInputStream(res);
+            InputStream is = res.getInputStream();
             byte[] fontstream = InputStreamToByteArray.convert(is);
             rt.free(loadedStreamAddr);
             loadedStreamAddr = rt.xmalloc(fontstream.length);
             byte[] fontstream = InputStreamToByteArray.convert(is);
             rt.free(loadedStreamAddr);
             loadedStreamAddr = rt.xmalloc(fontstream.length);
index 572823c..ed0e8a9 100644 (file)
@@ -46,7 +46,7 @@ public class Picture {
             new java.lang.Thread() { public void run() {
                 InputStream in = null;
                 try {
             new java.lang.Thread() { public void run() {
                 InputStream in = null;
                 try {
-                    in = b == null ? Stream.getInputStream(stream) : b.getImage();
+                    in = b == null ? stream.getInputStream() : b.getImage();
                 } catch (IOException e) { Log.error(Picture.class, e);
                 } catch (JSExn e) { Log.error(Picture.class, e);
                 }
                 } catch (IOException e) { Log.error(Picture.class, e);
                 } catch (JSExn e) { Log.error(Picture.class, e);
                 }
index 04d7edd..68fac37 100644 (file)
@@ -18,12 +18,16 @@ public abstract class JS {
     public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); }
     public JS get(JS key) throws JSExn { return null; }
     public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); }
     public Enumeration keys() throws JSExn { throw new JSExn("you can't enumerate the keys of this object (class=" + getClass().getName() +")"); }
     public JS get(JS key) throws JSExn { return null; }
     public void put(JS key, JS val) throws JSExn { throw new JSExn("" + key + " is read only (class=" + getClass().getName() +")"); }
+    
+    public InputStream getInputStream() throws IOException {
+        throw new IOException("this object doesn't have a stream associated with it " + getClass().getName() + ")");
+    }
         
     public final boolean hasTrap(JS key) { return getTrap(key) != null; }
     
     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("method not found (" + JS.debugToString(method) + ")");
         
     public final boolean hasTrap(JS key) { return getTrap(key) != null; }
     
     public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
         throw new JSExn("method not found (" + JS.debugToString(method) + ")");
-    }    
+    }
     
     // FIXME: JSArgs objects, pointers into stack frame
     public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
     
     // FIXME: JSArgs objects, pointers into stack frame
     public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
@@ -87,7 +91,7 @@ public abstract class JS {
         JS _unclone() { return clonee.unclone(); }
         public JS getClonee() { return clonee; }
         public Clone(JS clonee) throws JSExn {
         JS _unclone() { return clonee.unclone(); }
         public JS getClonee() { return clonee; }
         public Clone(JS clonee) throws JSExn {
-            if(!(clonee instanceof Cloneable)) throw new JSExn("" + getClass().getName() + " isn't cloneable");
+            if(!(clonee instanceof Cloneable)) throw new JSExn("" + clonee.getClass().getName() + " isn't cloneable");
             this.clonee = clonee;
         }
         public boolean jsequals(JS o) { return unclone().jsequals(o.unclone()); }
             this.clonee = clonee;
         }
         public boolean jsequals(JS o) { return unclone().jsequals(o.unclone()); }
@@ -100,6 +104,7 @@ public abstract class JS {
         public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
             return clonee.call(a0, a1, a2, rest, nargs);
         }
         public JS call(JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
             return clonee.call(a0, a1, a2, rest, nargs);
         }
+        public InputStream getInputStream() throws IOException { return clonee.getInputStream(); }
     }
     
     // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
     }
     
     // Static Interpreter Control Methods ///////////////////////////////////////////////////////////////
index e69c28c..8ed5759 100644 (file)
@@ -17,21 +17,24 @@ public abstract class Stream extends JS implements JS.Cloneable {
 
     // Public Interface //////////////////////////////////////////////////////////////////////////////
 
 
     // Public Interface //////////////////////////////////////////////////////////////////////////////
 
-    // FIXME: This should be in JS, "everything has a stream"
-    public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();}
+    /*public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();}*/
     public static class NotCacheableException extends Exception { }
 
     private Cache getCache = new Cache(100);
     public static class NotCacheableException extends Exception { }
 
     private Cache getCache = new Cache(100);
-    // FEATURE: Mandate that Streams use only String keys?
-    protected JS _get(JS key) throws JSExn { return null; }
+    public abstract JS _get(String key);
     public final JS get(JS key) throws JSExn {
         JS ret = (JS) getCache.get(key);
     public final JS get(JS key) throws JSExn {
         JS ret = (JS) getCache.get(key);
-        if (ret == null) getCache.put(key, ret = _get(key));
+        if (ret == null) getCache.put(key, ret = _get(JS.toString(key)));
         return ret;
     }
 
     // Private Interface //////////////////////////////////////////////////////////////////////////////
 
         return ret;
     }
 
     // Private Interface //////////////////////////////////////////////////////////////////////////////
 
+    static String getCacheKey(JS s) throws NotCacheableException {
+        if(s instanceof Stream) return ((Stream)s).getCacheKey();
+        throw new NotCacheableException();
+    }
+    
     public abstract InputStream getInputStream() throws IOException;
     protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
 
     public abstract InputStream getInputStream() throws IOException;
     protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
 
@@ -39,9 +42,8 @@ public abstract class Stream extends JS implements JS.Cloneable {
     // FEATURE: Only instansiate only ibex.net.HTTP, share with all substreams
     public static class HTTP extends Stream {
         private String url;
     // FEATURE: Only instansiate only ibex.net.HTTP, share with all substreams
     public static class HTTP extends Stream {
         private String url;
-        public String coerceToString() { return "Stream.HTTP:" + url; }
         public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
         public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
-        public JS _get(JS key) throws JSExn { return new HTTP(url + "/" + JS.toString(key)); }
+        public JS _get(String key) { return new HTTP(url + "/" + key); }
         public String getCacheKey(Vec path) throws NotCacheableException { return url; }
         public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); }
     }
         public String getCacheKey(Vec path) throws NotCacheableException { return url; }
         public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); }
     }
@@ -54,30 +56,30 @@ public abstract class Stream extends JS implements JS.Cloneable {
         public String getCacheKey() throws NotCacheableException {
             if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
         public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
         public String getCacheKey() throws NotCacheableException {
             if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
         public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
+        public JS _get(String key) { return null; }
     }
 
     /** a file */
     public static class File extends Stream {
         private String path;
         public File(String path) { this.path = path; }
     }
 
     /** a file */
     public static class File extends Stream {
         private String path;
         public File(String path) { this.path = path; }
-        public String coerceToString() { return "file:" + path; }
         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
         public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
         public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
-        public JS _get(JS key) throws JSExn { return new File(path + java.io.File.separatorChar + JS.toString(key)); }
+        public JS _get(String key) { return new File(path + java.io.File.separatorChar + key); }
     }
 
     /** "unwrap" a Zip archive */
     public static class Zip extends Stream {
     }
 
     /** "unwrap" a Zip archive */
     public static class Zip extends Stream {
-        private Stream parent;
+        private JS parent;
         private String path;
         private String path;
-        public Zip(Stream parent) { this(parent, null); }
-        public Zip(Stream parent, String path) {
+        public Zip(JS parent) { this(parent, null); }
+        public Zip(JS parent, String path) {
             while(path != null && path.startsWith("/")) path = path.substring(1);
             this.parent = parent;
             this.path = path;
         }
             while(path != null && path.startsWith("/")) path = path.substring(1);
             this.parent = parent;
             this.path = path;
         }
-        public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
-        public JS _get(JS key) throws JSExn { return new Zip(parent, path==null?JS.toString(key):path+'/'+JS.toString(key)); }
+        public String getCacheKey() throws NotCacheableException { return getCacheKey(parent) + "!zip:"; }
+        public JS _get(String key) { return new Zip(parent, path==null?key:path+'/'+key); }
         public InputStream getInputStream() throws IOException {
             InputStream pis = parent.getInputStream();
             ZipInputStream zis = new ZipInputStream(pis);
         public InputStream getInputStream() throws IOException {
             InputStream pis = parent.getInputStream();
             ZipInputStream zis = new ZipInputStream(pis);
@@ -90,12 +92,12 @@ public abstract class Stream extends JS implements JS.Cloneable {
 
     /** "unwrap" a Cab archive */
     public static class Cab extends Stream {
 
     /** "unwrap" a Cab archive */
     public static class Cab extends Stream {
-        private Stream parent;
+        private JS parent;
         private String path;
         private String path;
-        public Cab(Stream parent) { this(parent, null); }
-        public Cab(Stream parent, String path) { this.parent = parent; this.path = path; }
-        public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
-        public JS _get(JS key) throws JSExn { return new Cab(parent, path==null?JS.toString(key):path+'/'+JS.toString(key)); }
+        public Cab(JS parent) { this(parent, null); }
+        public Cab(JS parent, String path) { this.parent = parent; this.path = path; }
+        public String getCacheKey() throws NotCacheableException { return getCacheKey(parent) + "!cab:"; }
+        public JS _get(String key) { return new Cab(parent, path==null?key:path+'/'+key); }
         public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
     }
 
         public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
     }
 
@@ -103,14 +105,15 @@ public abstract class Stream extends JS implements JS.Cloneable {
     public static class Builtin extends Stream {
         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
         public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
     public static class Builtin extends Stream {
         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
         public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
+        public JS _get(String key) { return null; }
     }
 
     /** shadow resource which replaces the graft */
     public static class ProgressWatcher extends Stream {
     }
 
     /** shadow resource which replaces the graft */
     public static class ProgressWatcher extends Stream {
-        final Stream watchee;
+        final JS watchee;
         JS callback;
         JS callback;
-        public ProgressWatcher(Stream watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
-        public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
+        public ProgressWatcher(JS watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
+        public String getCacheKey() throws NotCacheableException { return getCacheKey(watchee); }
         public InputStream getInputStream() throws IOException {
             final InputStream is = watchee.getInputStream();
             return new FilterInputStream(is) {
         public InputStream getInputStream() throws IOException {
             final InputStream is = watchee.getInputStream();
             return new FilterInputStream(is) {
@@ -131,28 +134,32 @@ public abstract class Stream extends JS implements JS.Cloneable {
                     }
                 };
         }
                     }
                 };
         }
+        public JS _get(String s) { return null; }
     }
 
     /** subclass from this if you want a CachedInputStream for each path */
     public static class CachedStream extends Stream {
     }
 
     /** subclass from this if you want a CachedInputStream for each path */
     public static class CachedStream extends Stream {
-        private Stream parent;
+        private JS parent;
         private boolean disk = false;
         private String key;
         private boolean disk = false;
         private String key;
+        private String s;
         public String getCacheKey() throws NotCacheableException { return key; }
         CachedInputStream cis = null;
         public String getCacheKey() throws NotCacheableException { return key; }
         CachedInputStream cis = null;
-        public CachedStream(Stream p, String s, boolean d) throws NotCacheableException {
-            this.parent = p; this.disk = d; this.key = p.getCacheKey();
+        public CachedStream(JS p, String s, boolean d) throws NotCacheableException {
+            this.parent = p; this.s = s; this.disk = d; this.key = getCacheKey(p);
         }
         public InputStream getInputStream() throws IOException {
             if (cis != null) return cis.getInputStream();
             if (!disk) {
                 cis = new CachedInputStream(parent.getInputStream());
             } else {
         }
         public InputStream getInputStream() throws IOException {
             if (cis != null) return cis.getInputStream();
             if (!disk) {
                 cis = new CachedInputStream(parent.getInputStream());
             } else {
+                // FEATURE: Move LocalStorage into org.ibex.js or move this out
                 java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
                 if (f.exists()) return new FileInputStream(f);
                 cis = new CachedInputStream(parent.getInputStream(), f);
             }
             return cis.getInputStream();
         }
                 java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
                 if (f.exists()) return new FileInputStream(f);
                 cis = new CachedInputStream(parent.getInputStream(), f);
             }
             return cis.getInputStream();
         }
+        public JS _get(String s) { return null; }
     }
 }
     }
 }