1 // Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
5 import java.util.zip.*;
6 import org.ibex.util.*;
7 import org.ibex.plat.*;
11 * Essentiall an InputStream "factory". You can repeatedly ask a
12 * Stream for an InputStream, and each InputStream you get back will
13 * be totally independent of the others (ie separate stream position
14 * and state) although they draw from the same data source.
16 // FEATURE: Should this be in org.ibex.js?
17 public abstract class Stream extends JS implements JS.Cloneable {
19 // Public Interface //////////////////////////////////////////////////////////////////////////////
21 /*public static InputStream getInputStream(JS js) throws IOException { return ((Stream)js.unclone()).getInputStream();}*/
22 public static class NotCacheableException extends Exception { }
24 private Cache getCache = new Cache(100);
25 public abstract JS _get(String key);
26 public final JS get(JS key) throws JSExn {
27 JS ret = (JS) getCache.get(key);
28 if (ret == null) getCache.put(key, ret = _get(JS.toString(key)));
32 // Private Interface //////////////////////////////////////////////////////////////////////////////
34 static String getCacheKey(JS s) throws NotCacheableException {
35 if(s instanceof Stream) return ((Stream)s).getCacheKey();
36 throw new NotCacheableException();
39 public abstract InputStream getInputStream() throws IOException;
40 protected String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
42 /** HTTP or HTTPS resource */
43 // FEATURE: Only instansiate only ibex.net.HTTP, share with all substreams
44 public static class HTTP extends Stream {
46 public HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
47 public JS _get(String key) { return new HTTP(url + "/" + key); }
48 public String getCacheKey(Vec path) throws NotCacheableException { return url; }
49 public InputStream getInputStream() throws IOException { return new org.ibex.net.HTTP(url).GET(); }
53 public static class ByteArray extends Stream {
55 private String cacheKey;
56 public ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
57 public String getCacheKey() throws NotCacheableException {
58 if (cacheKey == null) throw new NotCacheableException(); return cacheKey; }
59 public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(bytes); }
60 public JS _get(String key) { return null; }
64 public static class File extends Stream {
66 public File(String path) { this.path = path; }
67 public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); /* already on disk */ }
68 public InputStream getInputStream() throws IOException { return new FileInputStream(path); }
69 public JS _get(String key) { return new File(path + java.io.File.separatorChar + key); }
72 /** "unwrap" a Zip archive */
73 public static class Zip extends Stream {
76 public Zip(JS parent) { this(parent, null); }
77 public Zip(JS parent, String path) {
78 while(path != null && path.startsWith("/")) path = path.substring(1);
82 public String getCacheKey() throws NotCacheableException { return getCacheKey(parent) + "!zip:"; }
83 public JS _get(String key) { return new Zip(parent, path==null?key:path+'/'+key); }
84 public InputStream getInputStream() throws IOException {
85 InputStream pis = parent.getInputStream();
86 ZipInputStream zis = new ZipInputStream(pis);
87 ZipEntry ze = zis.getNextEntry();
88 while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
89 if (ze == null) throw new IOException("requested file (" + path + ") not found in archive");
90 return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
94 /** "unwrap" a Cab archive */
95 public static class Cab extends Stream {
98 public Cab(JS parent) { this(parent, null); }
99 public Cab(JS parent, String path) { this.parent = parent; this.path = path; }
100 public String getCacheKey() throws NotCacheableException { return getCacheKey(parent) + "!cab:"; }
101 public JS _get(String key) { return new Cab(parent, path==null?key:path+'/'+key); }
102 public InputStream getInputStream() throws IOException { return new MSPack(parent.getInputStream()).getInputStream(path); }
105 /** the Builtin resource */
106 public static class Builtin extends Stream {
107 public String getCacheKey() throws NotCacheableException { throw new NotCacheableException(); }
108 public InputStream getInputStream() throws IOException { return Platform.getBuiltinInputStream(); }
109 public JS _get(String key) { return null; }
112 /** shadow resource which replaces the graft */
113 public static class ProgressWatcher extends Stream {
116 public ProgressWatcher(JS watchee, JS callback) { this.watchee = watchee; this.callback = callback; }
117 public String getCacheKey() throws NotCacheableException { return getCacheKey(watchee); }
118 public InputStream getInputStream() throws IOException {
119 final InputStream is = watchee.getInputStream();
120 return new FilterInputStream(is) {
121 int bytesDownloaded = 0;
122 public int read() throws IOException {
123 int ret = super.read();
124 if (ret != -1) bytesDownloaded++;
127 public int read(byte[] b, int off, int len) throws IOException {
128 int ret = super.read(b, off, len);
129 if (ret != 1) bytesDownloaded += ret;
130 Scheduler.add(new Task() { public void perform() throws IOException, JSExn {
131 callback.call(N(bytesDownloaded),
132 N(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0), null, null, 2);
138 public JS _get(String s) { return null; }
141 /** subclass from this if you want a CachedInputStream for each path */
142 public static class CachedStream extends Stream {
144 private boolean disk = false;
147 public String getCacheKey() throws NotCacheableException { return key; }
148 CachedInputStream cis = null;
149 public CachedStream(JS p, String s, boolean d) throws NotCacheableException {
150 this.parent = p; this.s = s; this.disk = d; this.key = getCacheKey(p);
152 public InputStream getInputStream() throws IOException {
153 if (cis != null) return cis.getInputStream();
155 cis = new CachedInputStream(parent.getInputStream());
157 // FEATURE: Move LocalStorage into org.ibex.js or move this out
158 java.io.File f = org.ibex.core.LocalStorage.Cache.getCacheFileForKey(key);
159 if (f.exists()) return new FileInputStream(f);
160 cis = new CachedInputStream(parent.getInputStream(), f);
162 return cis.getInputStream();
164 public JS _get(String s) { return null; }