402f2d635d4db8f759b958c6ca2a998731d2e4da
[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
10 // FIXME: ByteStream fileName property
11 /** base class for XWT resources */
12 public abstract class Res extends JS {
13
14     public final InputStream getInputStream() throws IOException { return getInputStream(""); }
15
16     public Res graft(Object newResource) { throw new JS.Exn("cannot graft onto this resource"); }
17
18     private Hash refCache = null;
19     public Object get(Object key) {
20         if ("".equals(key)) {
21             try {
22                 Res who = this;
23                 //who = addExtension(".xwt");
24                 // FIXME: cache templates by the Res that created them, not their nodename
25                 Template.buildTemplate(who.getInputStream(), "Resource");
26                 // FIXME: return the static here
27                 return null;
28             } catch (IOException e) {
29                 Log.logJS(this, e);
30                 return null;
31             }
32         }
33         Object ret = refCache == null ? null : refCache.get(key);
34         if (ret != null) return ret;
35         ret = new Ref(this, key);
36         if (refCache == null) refCache = new Hash();
37         refCache.put(key, ret);
38         return ret;
39     }
40
41     public void put(Object key, Object val) { throw new JS.Exn("cannot put to a resource"); } 
42     public Object[] keys() { throw new JS.Exn("cannot enumerate a resource"); } 
43
44     public abstract InputStream getInputStream(String path) throws IOException;
45     //public abstract Res addExtension(String extension);
46
47     public static Res stringToRes(String url) {
48         if (url.indexOf('!') != -1)
49             return (Res)(new Zip(stringToRes(url.substring(0, url.lastIndexOf('!')))).get(url.substring(url.lastIndexOf('!') + 1)));
50         if (url.startsWith("http://")) return new HTTP(url);
51         if (url.startsWith("https://")) return new HTTP(url);
52         if (url.startsWith("file:")) return new File(url.substring(5));
53         if (url.startsWith("cab:")) return new CAB(stringToRes(url.substring(4)));
54         throw new JS.Exn("invalid resource specifier");
55     }
56
57     /** HTTP or HTTPS resource */
58     public static class HTTP extends Res {
59         private String url;
60         HTTP(String url) { this.url = url; }
61         public InputStream getInputStream(String path) throws IOException { return new org.xwt.HTTP(url + path).GET(); }
62     }
63
64     // FIXME: dangerous
65     /** a file */
66     public static class File extends Res {
67         private String path;
68         File(String path) { this.path = path; }
69         public InputStream getInputStream(String rest) throws IOException { return new FileInputStream((path + rest).replace('/', java.io.File.separatorChar)); }
70     }
71
72     /** wrap a Res around a preexisting InputStream */
73     public static class IS extends Res {
74         InputStream parent;
75         IS(InputStream parent) { this.parent = parent; }
76         public InputStream getInputStream(String path) {
77             if (!"".equals(path)) throw new JS.Exn("can't access subresources of IS");
78             return parent;
79         }
80     }
81
82     /** "unwrap" a Zip archive */
83     public static class Zip extends Res {
84         private Res parent;
85         Zip(Res parent) { this.parent = parent; }
86         public InputStream getInputStream(String path) throws IOException {
87             ZipInputStream zis = new ZipInputStream(parent.getInputStream());
88             ZipEntry ze = zis.getNextEntry();
89             while(ze != null && !ze.getName().equals(path)) ze = zis.getNextEntry();
90             if (ze == null) throw new JS.Exn("zip file not found in archive");
91             return zis;
92         }
93     }
94
95     /** what you get when you reference a subresource */
96     public static class Ref extends Res {
97         Res parent;
98         Object key;
99         Ref(Res parent, Object key) { this.parent = parent; this.key = key; }
100         public InputStream getInputStream(String path) throws IOException {
101             return parent.getInputStream("/" + key + path);
102         }
103         public Res graft(Object newResource) { return new Graft(parent, key, newResource); }
104     }
105
106     /** shadow resource which replaces the graft */
107     public static class Graft extends Res {
108         Res graftee;
109         Object replaced_key;
110         Object replaced_val;
111         Graft(Res graftee, Object key, Object val) {
112             this.graftee = graftee; replaced_key = key; replaced_val = val; }
113         public boolean equals(Object o) { return (this == o || graftee.equals(o)); }
114         public int hashCode() { return graftee.hashCode(); }
115         public InputStream getInputStream(String s) throws IOException { return graftee.getInputStream(s); }
116         public Object get(Object key) {
117             return replaced_key.equals(key) ? replaced_val : graftee.get(key);
118         }
119     }
120
121     /** unpacks a Microsoft CAB file (possibly embedded in another file; we scan for 'MSCF' */
122     public static class CAB extends Res {
123         private Res parent;
124         CAB(Res parent) { this.parent = parent; }
125         private int swap_endian(int i) {
126             return ((i & 0xff) << 24) | ((i & 0xff00) << 8) | ((i & 0xff0000) >>> 8) | (i >>> 24);
127         }
128         public InputStream getInputStream(String path) throws IOException {
129             InputStream is = parent.getInputStream();
130             byte[] scan = new byte[4];
131             int ofs = 0;
132             for(int i=0; i<2; i++)  {
133                 // wierdly, .exe files have three MSCF's
134                 while(scan[0] != 'M' || scan[1] != 'S' || scan[2] != 'C' || scan[3] != 'F') {
135                     System.arraycopy(scan, 1, scan, 0, 3);
136                     int read = is.read();
137                     if (read == -1) throw new JS.Exn("MSCF header tag not found in file");
138                     scan[3] = (byte)read;
139                     ofs++;
140                 }
141                 scan[0] = 0;
142             }
143             Log.log(this, "found MSCF header at offset " + ofs);
144             return org.xwt.util.CAB.getFileInputStream(is, path, true);
145         }
146     }
147
148     public Object callMethod(Object method, Array args, boolean checkOnly) throws JS.Exn {
149         if (method.equals("getUTF")) {
150             if (checkOnly) return Boolean.TRUE;
151             if (args.length() != 0) return null;
152             try {
153                 CharArrayWriter caw = new CharArrayWriter();
154                 InputStream is = getInputStream();
155                 BufferedReader r = new BufferedReader(new InputStreamReader(is));
156                 char[] buf = new char[1024];
157                 while(true) {
158                     int numread = r.read(buf, 0, 1024);
159                     if (numread == -1) break;
160                     caw.write(buf, 0, numread);
161                 }
162                 return caw.toString();
163             } catch (IOException e) {
164                 if (Log.on) Log.log(Res.class, "IO Exception while reading from file");
165                 if (Log.on) Log.log(Res.class, e);
166                 throw new JS.Exn("error while reading from Resource");
167             }
168         } else if (method.equals("getDOM")) {
169             if (checkOnly) return Boolean.TRUE;
170             if (args.length() != 0) return null;
171             return new XMLHelper().doParse();
172         }
173         if (checkOnly) return Boolean.FALSE;
174         return null;
175     }
176
177     private class XMLHelper extends XML {
178         Vector obStack = new Vector();
179         public XMLHelper() { super(BUFFER_SIZE); }
180         public void startElement(XML.Element c) throws XML.SchemaException {
181             JS o = new JS.Obj();
182             o.put("$name", c.localName);
183             for(int i=0; i<c.len; i++) o.put(c.keys[i], c.vals[i]);
184             o.put("$numchildren", new Integer(0));
185             obStack.addElement(o);
186         }
187         public void endElement(XML.Element c) throws XML.SchemaException {
188             if (obStack.size() == 1) return;
189             JS me = (JS)obStack.lastElement();
190             obStack.setSize(obStack.size() - 1);
191             JS parent = (JS)obStack.lastElement();
192             int numchildren = ((Integer)parent.get("$numchildren")).intValue();
193             parent.put("$numchildren", new Integer(numchildren + 1));
194             parent.put(new Integer(numchildren), me);
195         }
196         public void characters(char[] ch, int start, int length) throws XML.SchemaException {
197             String s = new String(ch, start, length);
198             JS parent = (JS)obStack.lastElement();
199             int numchildren = ((Integer)parent.get("$numchildren")).intValue();
200             Object lastChild = parent.get(new Integer(numchildren - 1));
201             if (lastChild instanceof String) {
202                 parent.put(new Integer(numchildren - 1), lastChild + s);
203             } else {
204                 parent.put("$numchildren", new Integer(numchildren + 1));
205                 parent.put(new Integer(numchildren), s);
206             }
207         }
208         public void whitespace(char[] ch, int start, int length) {}
209         public JS doParse() throws JS.Exn {
210             try { 
211                 InputStream is = getInputStream();
212                 BufferedReader r = new BufferedReader(new InputStreamReader(is));
213                 parse(r);
214             } catch (XML.XMLException e) {
215                 throw new JS.Exn("error parsing XML: " + e.toString());
216             } catch (IOException e) {
217                 if (Log.on) Log.log(this, "IO Exception while reading from file");
218                 if (Log.on) Log.log(this, e);
219                 throw new JS.Exn("error reading from Resource");
220             }
221             return obStack.size() >= 1 ? (JS)obStack.elementAt(0) : null;
222         }
223     }
224
225     public void writeTo(OutputStream os) throws IOException {
226         InputStream is = getInputStream();
227         byte[] buf = new byte[1024];
228         while(true) {
229             int numread = is.read(buf, 0, 1024);
230             if (numread == -1) break;
231             if (Log.on) Log.log(this, "wrote " + numread + " bytes");
232             os.write(buf, 0, numread);
233         }
234         os.flush();
235
236         // we have to close this because flush() doesn't work on Win32-GCJ
237         os.close();
238     }
239 }