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