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