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