JAL-1807 Bob's JalviewJS prototype first commit
[jalviewjs.git] / src / javajs / util / OC.java
1 package javajs.util;\r
2 \r
3 import java.io.BufferedWriter;\r
4 import java.io.ByteArrayOutputStream;\r
5 import java.io.FileOutputStream;\r
6 import java.io.IOException;\r
7 import java.io.OutputStream;\r
8 import java.io.OutputStreamWriter;\r
9 \r
10 \r
11 \r
12 import javajs.J2SIgnoreImport;\r
13 import javajs.api.BytePoster;\r
14 import javajs.api.JmolObjectInterface;\r
15 \r
16 /**\r
17  * \r
18  * A generic output method. JmolOutputChannel can be used to:\r
19  * \r
20  * add characters to a StringBuffer \r
21  *   using fileName==null, append() and toString()\r
22  *   \r
23  * add bytes utilizing ByteArrayOutputStream \r
24  *   using writeBytes(), writeByteAsInt(), append()*, and bytesAsArray()\r
25  *       *append() can be used as long as os==ByteArrayOutputStream\r
26  *        or it is not used before one of the writeByte methods. \r
27  * \r
28  * output characters to a FileOutputStream \r
29  *  using os==FileOutputStream, asWriter==true, append(), and closeChannel()\r
30  *  \r
31  * output bytes to a FileOutputStream \r
32  *  using os==FileOutputStream, writeBytes(), writeByteAsInt(), append(), and closeChannel()\r
33  * \r
34  * post characters or bytes to a remote server\r
35  *  using fileName=="http://..." or "https://...",\r
36  *    writeBytes(), writeByteAsInt(), append(), and closeChannel()\r
37  *    \r
38  * send characters or bytes to a JavaScript function\r
39  *  when JavaScript and (typeof fileName == "function")\r
40  *  \r
41  * if fileName equals ";base64,", then the data are base64-encoded\r
42  * prior to writing, and closeChannel() returns the data.\r
43  * \r
44  *  @author hansonr  Bob Hanson hansonr@stolaf.edu  9/2013\r
45  *  \r
46  *  \r
47  */\r
48 \r
49 @J2SIgnoreImport({ java.io.FileOutputStream.class })\r
50 public class OC extends OutputStream {\r
51  \r
52   private BytePoster bytePoster; // only necessary for writing to http:// or https://\r
53   private String fileName;\r
54   private BufferedWriter bw;\r
55   private boolean isLocalFile;\r
56   private int byteCount;\r
57   private boolean isCanceled;\r
58   private boolean closed;\r
59   private OutputStream os;\r
60   private SB sb;\r
61   private String type;\r
62         private boolean isBase64;\r
63         private OutputStream os0;\r
64         private byte[] bytes; // preset bytes; output only\r
65   \r
66   public OC setParams(BytePoster bytePoster, String fileName,\r
67                                      boolean asWriter, OutputStream os) {\r
68     this.bytePoster = bytePoster;\r
69     this.fileName = fileName;\r
70     isBase64 = ";base64,".equals(fileName);\r
71     if (isBase64) {\r
72         fileName = null;\r
73         os0 = os;\r
74         os = null;\r
75     }\r
76     this.os = os;\r
77     isLocalFile = (fileName != null && !isRemote(fileName));\r
78     if (asWriter && !isBase64 && os != null)\r
79       bw = new BufferedWriter(new OutputStreamWriter(os));\r
80     return this;\r
81   }\r
82 \r
83   public OC setBytes(byte[] b) {\r
84         bytes = b;\r
85         return this;\r
86   }\r
87   \r
88   public String getFileName() {\r
89     return fileName;\r
90   }\r
91   \r
92   public String getName() {\r
93     return (fileName == null ? null : fileName.substring(fileName.lastIndexOf("/") + 1));\r
94   }\r
95 \r
96   public int getByteCount() {\r
97     return byteCount;\r
98   }\r
99 \r
100   /**\r
101    * \r
102    * @param type  user-identified type (PNG, JPG, etc)\r
103    */\r
104   public void setType(String type) {\r
105     this.type = type;\r
106   }\r
107   \r
108   public String getType() {\r
109     return type;\r
110   }\r
111 \r
112   /**\r
113    * will go to string buffer if bw == null and os == null\r
114    * \r
115    * @param s\r
116    * @return this, for chaining like a standard StringBuffer\r
117    * \r
118    */\r
119   public OC append(String s) {\r
120     try {\r
121       if (bw != null) {\r
122         bw.write(s);\r
123       } else if (os == null) {\r
124         if (sb == null)\r
125           sb = new SB();\r
126         sb.append(s);\r
127       } else {\r
128         byte[] b = s.getBytes();\r
129         os.write(b, 0, b.length);\r
130         byteCount += b.length;\r
131         return this;\r
132       }\r
133     } catch (IOException e) {\r
134       // ignore\r
135     }\r
136     byteCount += s.length(); // not necessarily exactly correct if unicode\r
137     return this;\r
138   }\r
139 \r
140   public void reset() {\r
141     sb = null;\r
142     initOS();\r
143   }\r
144 \r
145 \r
146   private void initOS() {\r
147     if (sb != null) {\r
148       String s = sb.toString();\r
149       reset();\r
150       append(s);\r
151       return;\r
152     }\r
153     try {\r
154       /**\r
155        * @j2sNative\r
156        * \r
157        *            this.os = null;\r
158        */\r
159       {\r
160         if (os instanceof FileOutputStream) {\r
161           os.close();\r
162           os = new FileOutputStream(fileName);\r
163         } else {\r
164           os = null;\r
165         }\r
166       }\r
167       if (os == null)\r
168         os = new ByteArrayOutputStream();\r
169       if (bw != null) {\r
170         bw.close();\r
171         bw = new BufferedWriter(new OutputStreamWriter(os));\r
172       }\r
173     } catch (Exception e) {\r
174       // not perfect here.\r
175       System.out.println(e.toString());\r
176     }\r
177     byteCount = 0;\r
178   }\r
179 \r
180   /**\r
181    * @j2sOverride\r
182    */\r
183   @Override\r
184   public void write(byte[] buf, int i, int len) {\r
185     if (os == null)\r
186       initOS();\r
187     try {\r
188       os.write(buf, i, len);\r
189     } catch (IOException e) {\r
190     }\r
191     byteCount += len;\r
192   }\r
193   \r
194   /**\r
195    * @param b  \r
196    */\r
197   public void writeByteAsInt(int b) {\r
198     if (os == null)\r
199       initOS();\r
200     /**\r
201      * @j2sNative\r
202      * \r
203      *  this.os.writeByteAsInt(b);\r
204      * \r
205      */\r
206     {\r
207       try {\r
208         os.write(b);\r
209       } catch (IOException e) {\r
210       }\r
211     }\r
212     byteCount++;\r
213   }\r
214   \r
215   /**\r
216    * Will break JavaScript if used.\r
217    * \r
218    * @j2sIgnore\r
219    * \r
220    * @param b\r
221    */\r
222   @Override\r
223   @Deprecated\r
224   public void write(int b) {\r
225     // required by standard ZipOutputStream -- do not use, as it will break JavaScript methods\r
226     if (os == null)\r
227       initOS();\r
228     try {\r
229       os.write(b);\r
230     } catch (IOException e) {\r
231     }\r
232     byteCount++;\r
233   }\r
234 \r
235 //  /**\r
236 //   * Will break if used; no equivalent in JavaScript.\r
237 //   * \r
238 //   * @j2sIgnore\r
239 //   * \r
240 //   * @param b\r
241 //   */\r
242 //  @Override\r
243 //  @Deprecated\r
244 //  public void write(byte[] b) {\r
245 //    // not used in JavaScript due to overloading problem there\r
246 //    write(b, 0, b.length);\r
247 //  }\r
248 \r
249   public void cancel() {\r
250     isCanceled = true;\r
251     closeChannel();\r
252   }\r
253 \r
254   @SuppressWarnings({ "null", "unused" })\r
255   public String closeChannel() {\r
256     if (closed)\r
257       return null;\r
258     // can't cancel file writers\r
259     try {\r
260       if (bw != null) {\r
261         bw.flush();\r
262         bw.close();\r
263       } else if (os != null) {\r
264         os.flush();\r
265         os.close();\r
266       }\r
267       if (os0 != null && isCanceled) {\r
268         os0.flush();\r
269         os0.close();\r
270       }\r
271     } catch (Exception e) {\r
272       // ignore closing issues\r
273     }\r
274     if (isCanceled) {\r
275       closed = true;\r
276       return null;\r
277     }\r
278     if (fileName == null) {\r
279       if (isBase64) {\r
280         String s = getBase64();\r
281         if (os0 != null) {\r
282           os = os0;\r
283           append(s);\r
284         }\r
285         sb = new SB();\r
286         sb.append(s);\r
287         isBase64 = false;\r
288         return closeChannel();\r
289       }\r
290       return (sb == null ? null : sb.toString());\r
291     }\r
292     closed = true;\r
293     JmolObjectInterface jmol = null;\r
294     Object _function = null;\r
295     /**\r
296      * @j2sNative\r
297      * \r
298      *            jmol = Jmol; _function = (typeof this.fileName == "function" ?\r
299      *            this.fileName : null);\r
300      * \r
301      */\r
302     {\r
303       if (!isLocalFile) {\r
304         String ret = postByteArray(); // unsigned applet could do this\r
305         if (ret.startsWith("java.net"))\r
306           byteCount = -1;\r
307         return ret;\r
308       }\r
309     }\r
310     if (jmol != null) {\r
311       Object data = (sb == null ? toByteArray() : sb.toString());\r
312       if (_function == null)\r
313         jmol._doAjax(fileName, null, data);\r
314       else\r
315         jmol._apply(fileName, data);\r
316     }\r
317     return null;\r
318   }\r
319 \r
320         public boolean isBase64() {\r
321                 return isBase64;\r
322         }\r
323 \r
324         public String getBase64() {\r
325     return Base64.getBase64(toByteArray()).toString();\r
326         }\r
327         \r
328   public byte[] toByteArray() {\r
329     return (bytes != null ? bytes : os instanceof ByteArrayOutputStream ? ((ByteArrayOutputStream)os).toByteArray() : null);\r
330   }\r
331 \r
332   @Override\r
333   @Deprecated\r
334   public void close() {\r
335     closeChannel();\r
336   }\r
337 \r
338   @Override\r
339   public String toString() {\r
340     if (bw != null)\r
341       try {\r
342         bw.flush();\r
343       } catch (IOException e) {\r
344         // TODO\r
345       }\r
346     if (sb != null)\r
347       return closeChannel();\r
348     return byteCount + " bytes";\r
349   }\r
350 \r
351   private String postByteArray() {\r
352     byte[] bytes = (sb == null ? toByteArray() : sb.toString().getBytes());\r
353     return bytePoster.postByteArray(fileName, bytes);\r
354   }\r
355 \r
356   public final static String[] urlPrefixes = { "http:", "https:", "sftp:", "ftp:",\r
357   "file:" };\r
358   // note that SFTP is not supported\r
359   public final static int URL_LOCAL = 4;\r
360 \r
361   public static boolean isRemote(String fileName) {\r
362     if (fileName == null)\r
363       return false;\r
364     int itype = urlTypeIndex(fileName);\r
365     return (itype >= 0 && itype != URL_LOCAL);\r
366   }\r
367 \r
368   public static boolean isLocal(String fileName) {\r
369     if (fileName == null)\r
370       return false;\r
371     int itype = urlTypeIndex(fileName);\r
372     return (itype < 0 || itype == URL_LOCAL);\r
373   }\r
374 \r
375   public static int urlTypeIndex(String name) {\r
376     if (name == null)\r
377       return -2; // local unsigned applet\r
378     for (int i = 0; i < urlPrefixes.length; ++i) {\r
379       if (name.startsWith(urlPrefixes[i])) {\r
380         return i;\r
381       }\r
382     }\r
383     return -1;\r
384   }\r
385 \r
386 }\r