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