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;
9 import javajs.api.BytePoster;
10 import javajs.api.GenericOutputChannel;
11 import javajs.api.js.J2SObjectInterface;
15 * A generic output method. JmolOutputChannel can be used to:
17 * add characters to a StringBuffer
18 * using fileName==null, append() and toString()
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.
25 * output characters to a FileOutputStream
26 * using os==FileOutputStream, asWriter==true, append(), and closeChannel()
28 * output bytes to a FileOutputStream
29 * using os==FileOutputStream, writeBytes(), writeByteAsInt(), append(), and closeChannel()
31 * post characters or bytes to a remote server
32 * using fileName=="http://..." or "https://...",
33 * writeBytes(), writeByteAsInt(), append(), and closeChannel()
35 * send characters or bytes to a JavaScript function
36 * when JavaScript and (typeof fileName == "function")
38 * if fileName equals ";base64,", then the data are base64-encoded
39 * prior to writing, and closeChannel() returns the data.
41 * @author hansonr Bob Hanson hansonr@stolaf.edu 9/2013
46 public class OC extends OutputStream implements GenericOutputChannel {
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;
58 private boolean isBase64;
59 private OutputStream os0;
60 private byte[] bytes; // preset bytes; output only
62 public boolean bigEndian = true;
65 public boolean isBigEndian() {
69 public void setBigEndian(boolean TF) {
73 public OC setParams(BytePoster bytePoster, String fileName,
74 boolean asWriter, OutputStream os) {
75 this.bytePoster = bytePoster;
76 isBase64 = ";base64,".equals(fileName);
82 this.fileName = fileName;
84 isLocalFile = (fileName != null && !isRemote(fileName));
85 if (asWriter && !isBase64 && os != null)
86 bw = Rdr.getBufferedWriter(os, null);
90 public OC setBytes(byte[] b) {
95 public String getFileName() {
99 public String getName() {
100 return (fileName == null ? null : fileName.substring(fileName.lastIndexOf("/") + 1));
103 public int getByteCount() {
109 * @param type user-identified type (PNG, JPG, etc)
111 public void setType(String type) {
115 public String getType() {
120 * will go to string buffer if bw == null and os == null
123 * @return this, for chaining like a standard StringBuffer
126 public OC append(String s) {
130 } else if (os == null) {
135 byte[] b = s.getBytes();
136 os.write(b, 0, b.length);
137 byteCount += b.length;
140 } catch (IOException e) {
143 byteCount += s.length(); // not necessarily exactly correct if unicode
148 public void reset() {
154 private void initOS() {
156 String s = sb.toString();
168 if (os instanceof FileOutputStream) {
170 os = new FileOutputStream(fileName);
176 os = new ByteArrayOutputStream();
179 bw = Rdr.getBufferedWriter(os, null);
181 } catch (Exception e) {
183 System.out.println(e.toString());
192 public void writeByteAsInt(int b) {
198 } catch (IOException e) {
205 public void write(byte[] buf, int i, int len) {
209 os.write(buf, i, len);
210 } catch (IOException e) {
216 public void writeShort(short i) {
218 writeByteAsInt(i >> 8);
222 writeByteAsInt(i >> 8);
227 public void writeLong(long b) {
229 writeInt((int) ((b >> 32) & 0xFFFFFFFFl));
230 writeInt((int) (b & 0xFFFFFFFFl));
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);
243 public void write(int b) {
244 // required by standard ZipOutputStream -- do not use, as it will break JavaScript methods
249 } catch (IOException e) {
254 public void write(byte[] b) {
255 // not used in JavaScript due to overloading problem there
256 write(b, 0, b.length);
259 public void cancel() {
265 @SuppressWarnings({ "unused" })
266 public String closeChannel() {
269 // can't cancel file writers
274 } else if (os != null) {
278 if (os0 != null && isCanceled) {
282 } catch (Exception e) {
283 // ignore closing issues
289 if (fileName == null) {
291 String s = getBase64();
299 return closeChannel();
301 return (sb == null ? null : sb.toString());
304 J2SObjectInterface jmol = null;
305 Object _function = null;
309 * jmol = self.J2S || Jmol; _function = (typeof this.fileName == "function" ?
310 * this.fileName : null);
315 String ret = postByteArray(); // unsigned applet could do this
316 if (ret.startsWith("java.net"))
322 Object data = (sb == null ? toByteArray() : sb.toString());
323 if (_function == null)
324 jmol._doAjax(fileName, null, data, sb == null);
326 jmol._apply(_function, data);
331 public boolean isBase64() {
335 public String getBase64() {
336 return Base64.getBase64(toByteArray()).toString();
339 public byte[] toByteArray() {
340 return (bytes != null ? bytes : os instanceof ByteArrayOutputStream ? ((ByteArrayOutputStream)os).toByteArray() : null);
345 public void close() {
350 public String toString() {
354 } catch (IOException e) {
358 return closeChannel();
359 return byteCount + " bytes";
362 private String postByteArray() {
363 byte[] bytes = (sb == null ? toByteArray() : sb.toString().getBytes());
364 return bytePoster.postByteArray(fileName, bytes);
367 public final static String[] urlPrefixes = { "http:", "https:", "sftp:", "ftp:",
369 // note that SFTP is not supported
370 public final static int URL_LOCAL = 4;
372 public static boolean isRemote(String fileName) {
373 if (fileName == null)
375 int itype = urlTypeIndex(fileName);
376 return (itype >= 0 && itype != URL_LOCAL);
379 public static boolean isLocal(String fileName) {
380 if (fileName == null)
382 int itype = urlTypeIndex(fileName);
383 return (itype < 0 || itype == URL_LOCAL);
386 public static int urlTypeIndex(String name) {
388 return -2; // local unsigned applet
389 for (int i = 0; i < urlPrefixes.length; ++i) {
390 if (name.startsWith(urlPrefixes[i])) {
398 public void writeInt(int i) {
400 writeByteAsInt(i >> 24);
401 writeByteAsInt(i >> 16);
402 writeByteAsInt(i >> 8);
406 writeByteAsInt(i >> 8);
407 writeByteAsInt(i >> 16);
408 writeByteAsInt(i >> 24);
412 public void writeFloat(float x) {
413 writeInt(x == 0 ? 0 : Float.floatToIntBits(x));