acf617ac87faa3104aa5f8e5290a0a8684574593
[org.ibex.js.git] / src / org / ibex / js / Fountain.java
1 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.ibex.js;
3
4 import java.io.*;
5 import java.util.zip.*;
6 import org.ibex.util.*;
7 import org.ibex.net.*;
8
9 /**
10  *   Essentiall an InputStream "factory".  You can repeatedly ask a
11  *   Fountain for an InputStream, and each InputStream you get back will
12  *   be totally independent of the others (ie separate stream position
13  *   and state) although they draw from the same data source.
14  */
15 public abstract class Fountain extends JS.O implements JS.Cloneable {
16
17     // Public Interface //////////////////////////////////////////////////////////////////////////////
18
19     public static InputStream getInputStream(Object js) throws IOException { return ((Fountain)((JS)js).unclone()).getInputStream();}
20     public static class NotCacheableException extends Exception { }
21
22     // streams are "sealed" by default to prevent accidental object leakage
23     public void put(Object key, Object val) { }
24     private Cache getCache = new Cache(100);
25     protected Object _get(Object key) { return null; }
26     public final Object get(Object key) {
27         Object ret = getCache.get(key);
28         if (ret == null) getCache.put(key, ret = _get(key));
29         return ret;
30     }
31
32     // Private Interface //////////////////////////////////////////////////////////////////////////////
33
34     public abstract InputStream getInputStream() throws IOException;
35     protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
36
37     /** HTTP or HTTPS resource */
38     public static class HTTP extends Fountain {
39         private String url;
40         //public String toString() { return "Stream.HTTP:" + url; }
41         public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
42         public Object _get(Object key) { return new HTTP(url + "/" + (String)key); }
43         public String getCacheKey(Vec path) throws NotCacheableException { return url; }
44         public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(null, null); }
45     }
46
47     /** byte arrays */
48     public static class ByteArray extends Fountain {
49         private byte[] bytes;
50         private String cacheKey;
51         public ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
52         public String getCacheKey() throws NotCacheableException {
53             if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
54         public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
55     }
56
57     /** a file */
58     public static class File extends Fountain {
59         private String path;
60         public File(String path) { this.path = path; }
61         //public String toString() { return "file:" + path; }
62         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
63         public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
64         public Object _get(Object key) { return new File(path + java.io.File.separatorChar + (String)key); }
65     }
66
67     /** "unwrap" a Zip archive */
68     public static class Zip extends Fountain {
69         private Fountain parent;
70         private String path;
71         public Zip(Fountain parent) { this(parent, null); }
72         public Zip(Fountain parent, String path) {
73             while(path != null && path.startsWith("/")) path = path.substring(1);
74             this.parent = parent;
75             this.path = path;
76         }
77         public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
78         public Object _get(Object key) { return new Zip(parent, path==null?(String)key:path+'/'+(String)key); }
79         public InputStream getInputStream() throws IOException {
80             InputStream pis = parent.getInputStream();
81             ZipInputStream zis = new ZipInputStream(pis);
82             ZipEntry ze = zis.getNextEntry();
83             while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
84             if (ze == null) throw new IOException("requested file (" + path + ") not found in archive");
85             return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
86         }
87     }
88
89     /** "unwrap" a Cab archive */
90     /* temporarily disabled due to dependency issues
91     public static class Cab extends Fountain {
92         private Fountain parent;
93         private String path;
94         public Cab(Fountain parent) { this(parent, null); }
95         public Cab(Fountain parent, String path) { this.parent = parent; this.path = path; }
96         public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
97         public Object _get(Object key) { return new Cab(parent, path==null?(String)key:path+'/'+(String)key); }
98         public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
99     }
100     */
101
102     /** the Builtin resource */
103     /* temporarily disabled due to dependency issues
104     public static class Builtin extends Fountain {
105         public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
106         public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
107     }
108     */
109
110     /** shadow resource which replaces the graft */
111     /* temporarily disabled due to dependency issues
112     public static class ProgressWatcher extends Fountain {
113         final Fountain watchee;
114         JS callback;
115         public ProgressWatcher(Fountain watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
116         public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
117         public InputStream getInputStream() throws IOException {
118             final InputStream is = watchee.getInputStream();
119             return new FilterInputStream(is) {
120                     int bytesDownloaded = 0;
121                     public int read() throws IOException {
122                         int ret = super.read();
123                         if (ret != -1) bytesDownloaded++;
124                         return ret;
125                     }
126                     public int read(byte[] b, int off, int len) throws IOException {
127                         int ret = super.read(b, off, len);
128                         if (ret != 1) bytesDownloaded += ret;
129                         Scheduler.add(new Task() { public void perform() throws IOException, JSExn {
130                             callback.call(N(bytesDownloaded),
131                                           N(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0), null, null, 2);
132                         } });
133                         return ret;
134                     }
135                 };
136         }
137     }
138     */
139
140     /** subclass from this if you want a CachedInputStream for each path */
141     /* temporarily disabled due to dependency issues
142     public static class CachedStream extends Fountain {
143         private Fountain parent;
144         private boolean disk = false;
145         private String key;
146         public String getCacheKey() throws NotCacheableException { return key; }
147         CachedInputStream cis = null;
148         public CachedStream(Fountain p, String s, boolean d) throws NotCacheableException {
149             this.parent = p; this.disk = d; this.key = p.getCacheKey();
150         }
151         public InputStream getInputStream() throws IOException {
152             if (cis != null) return cis.getInputStream();
153             if (!disk) {
154                 cis = new CachedInputStream(parent.getInputStream());
155             } else {
156                 java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
157                 if (f.exists()) return new FileInputStream(f);
158                 cis = new CachedInputStream(parent.getInputStream(), f);
159             }
160             return cis.getInputStream();
161         }
162     }
163     */
164 }