f37d758610ed7e0006a7730e7dbe8a30fcd5b4b7
[org.ibex.core.git] / src / org / xwt / Res.java
1 // Copyright 2003 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.bouncycastle.util.encoders.Base64;
10
11
12 /** Base class for XWT resources */
13 public abstract class Res extends JS {
14
15     // Base Class //////////////////////////////////////////////////////////////////////
16
17     public String typeName() { return "resource"; }
18
19     /** so that we get the same subresource each time */
20     private Hash refCache = null;
21
22     public Template t = null;
23
24     public final InputStream getInputStream() throws IOException { return getInputStream(""); }
25     public abstract InputStream getInputStream(String path) throws IOException;
26
27     public Res addExtension(String extension) { return new Ref(this, extension); }
28
29     public Object get(Object key) {
30         if ("".equals(key)) {
31             Template t = Template.getTemplate(addExtension(".xwt"));
32             return t == null ? null : t.getStatic();
33         }
34         Object ret = refCache == null ? null : refCache.get(key);
35         if (ret != null) return ret;
36         ret = new Ref(this, key);
37         if (refCache == null) refCache = new Hash();
38         refCache.put(key, ret);
39         return ret;
40     }
41
42
43
44     // Caching //////////////////////////////////////////////////////////////////////
45
46     public static class NotCacheableException extends Exception { }
47     public static NotCacheableException notCacheable = new NotCacheableException();
48
49     /** if it makes sense to cache a resource, the resource must return a unique key */
50     public String getCacheKey() throws NotCacheableException { throw notCacheable; }
51
52     /** subclass from this if you want a CachedInputStream for each path */
53     public static class CachedRes extends Res {
54         private Res parent;
55         private boolean disk = false;
56         private String key;
57         public String getCacheKey() throws NotCacheableException { return key; }
58         public String toString() { return key; }
59         private Hash cachedInputStreams = new Hash();
60         public CachedRes(Res p, String s, boolean d) throws NotCacheableException {
61             this.parent = p; this.disk = d; this.key = p.getCacheKey();
62         }
63         public InputStream getInputStream(String path) throws IOException {
64             CachedInputStream cis = (CachedInputStream)cachedInputStreams.get(path);
65             if (cis == null) {
66                 java.io.File f = null;
67                 if (disk) {
68                     f = new java.io.File(System.getProperty("user.home") +
69                                          java.io.File.separatorChar + ".xwt" +
70                                          java.io.File.separatorChar + "caches" +
71                                          java.io.File.separatorChar +
72                                          new String(Base64.encode(key.getBytes())));
73                     Log.log(this, "caching resource in " + f);
74                     new java.io.File(f.getParent()).mkdirs();
75                     if (f.exists()) return new FileInputStream(f);
76                 }
77                 cis = new CachedInputStream(parent.getInputStream(path), f);
78                 cachedInputStreams.put(path, cis);
79             }
80             return cis.getInputStream();
81         }
82     }
83
84
85     // Useful Subclasses //////////////////////////////////////////////////////////////////////
86
87     /** HTTP or HTTPS resource */
88     public static class HTTP extends Res {
89         private String url;
90         HTTP(String url) { while (url.endsWith("/")) url = url.substring(0, url.length() - 1); this.url = url; }
91         public String toString() { return url; }
92         public String getCacheKey() throws NotCacheableException { return url; }
93         public InputStream getInputStream(String path) throws IOException { return new org.xwt.HTTP(url + path).GET(); }
94     }
95
96     /** byte arrays */
97     public static class ByteArray extends Res {
98         private byte[] bytes;
99         private String cacheKey = null;
100         ByteArray(byte[] bytes, String cacheKey) { this.bytes = bytes; this.cacheKey = cacheKey; }
101         public String toString() { return "byte[]"; }
102         public String getCacheKey() throws NotCacheableException { return cacheKey; }
103         public InputStream getInputStream(String path) throws IOException {
104             if (!"".equals(path)) throw new IOException("can't get subresources of a byte[] resource");
105             return new ByteArrayInputStream(bytes);
106         }
107     }
108
109     /** a file */
110     public static class File extends Res {
111         private String path;
112         File(String path) {
113             while (path.endsWith(java.io.File.separatorChar + "")) path = path.substring(0, path.length() - 1);
114             this.path = path;
115         }
116         public String toString() { return "file:" + path; }
117         public String getCacheKey() throws NotCacheableException { throw notCacheable; }  // already on the disk!
118         public InputStream getInputStream(String rest) throws IOException {
119             return new FileInputStream((path + rest).replace('/', java.io.File.separatorChar)); }
120     }
121
122     /** "unwrap" a Zip archive */
123     public static class Zip extends Res {
124         private Res parent;
125         Zip(Res parent) { this.parent = parent; }
126         public String toString() { return parent.toString() + "!zip"; }
127         public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!zip:"; }
128         public InputStream getInputStream(String path) throws IOException {
129             if (path.startsWith("/")) path = path.substring(1);
130             InputStream pis = parent.getInputStream();
131             ZipInputStream zis = new ZipInputStream(pis);
132             ZipEntry ze = zis.getNextEntry();
133             while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
134             if (ze == null) throw new IOException("requested file (" + path + ") not found in archive");
135             return new KnownLength.KnownLengthInputStream(zis, (int)ze.getSize());
136         }
137     }
138
139     /** "unwrap" a Cab archive */
140     public static class Cab extends Res {
141         private Res parent;
142         Cab(Res parent) { this.parent = parent; }
143         public String toString() { return parent.toString() + "!cab"; }
144         public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "!cab:"; }
145         public InputStream getInputStream(String path) throws IOException {
146             if (path.startsWith("/")) path = path.substring(1);
147             return new org.xwt.translators.MSPack(parent.getInputStream()).getInputStream(path);
148         }
149     }
150
151     /** the Builtin resource */
152     public static class Builtin extends Res {
153         public Builtin() { };
154         public String getCacheKey() throws NotCacheableException { throw notCacheable; }    // not cacheable
155         public String toString() { return "builtin:"; }
156         public InputStream getInputStream(String path) throws IOException {
157             if (!path.equals("")) throw new IOException("the builtin resource has no subresources");
158             return Platform.getBuiltinInputStream();
159         }
160     }
161
162     /** what you get when you reference a subresource */
163     public static class Ref extends Res {
164         Res parent;
165         Object key;
166         public String toString() { return parent.toString() + "/" + key; }
167         Ref(Res parent, Object key) { this.parent = parent; this.key = key; }
168         public String getCacheKey() throws NotCacheableException { return parent.getCacheKey() + "/" + key; }
169         public Res addExtension(String extension) {
170             return ((String)key).endsWith(extension) ? this : new Ref(parent, key + extension); }
171         public InputStream getInputStream(String path) throws IOException { return parent.getInputStream("/" + key + path); }
172     }
173
174     /** shadow resource which replaces the graft */
175     public static class ProgressWatcher extends Res {
176         final Res watchee;
177         JSFunction callback;
178         ProgressWatcher(Res watchee, JSFunction callback) { this.watchee = watchee; this.callback = callback; }
179         public String toString() { return watchee.toString(); }
180         public String getCacheKey() throws NotCacheableException { return watchee.getCacheKey(); }
181         public InputStream getInputStream(String s) throws IOException {
182             final InputStream is = watchee.getInputStream(s);
183             return new FilterInputStream(is) {
184                     int bytesDownloaded = 0;
185                     public int read() throws IOException {
186                         int ret = super.read();
187                         if (ret != -1) bytesDownloaded++;
188                         return ret;
189                     }
190                     public int read(byte[] b, int off, int len) throws IOException {
191                         int ret = super.read(b, off, len);
192                         if (ret != 1) bytesDownloaded += ret;
193                         Scheduler.add(new Scheduler.Task() { public void perform() throws Exception {
194                             callback.call(N(bytesDownloaded),
195                                           N(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0), null, null, 2);
196                         } });
197                         return ret;
198                     }
199                 };
200         }
201     }
202 }