package LBJ2.util; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.FileOutputStream; import java.io.FilterOutputStream; import java.io.OutputStream; import java.net.URL; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; /** * This class intends to operate just as a <code>DataOutputStream</code> with * some additional convenience methods and built-in exception handling. * * @author Nick Rizzolo **/ public class ExceptionlessOutputStream extends FilterOutputStream { /** This buffer is used internally by {@link #writeUTF(String)}. */ private byte[] buffer = null; /** The underlying data output stream. */ private DataOutputStream dos; /** * Opens a buffered (and uncompressed) stream for writing to the specified * file. * * @param filename The file to write to. * @return The newly opened stream. **/ public static ExceptionlessOutputStream openBufferedStream(String filename) { ExceptionlessOutputStream eos = null; try { eos = new ExceptionlessOutputStream( new BufferedOutputStream( new FileOutputStream(filename))); } catch (Exception e) { System.err.println("Can't open '" + filename + "' for output:"); e.printStackTrace(); System.exit(1); } return eos; } /** * Opens a compressed stream for writing to the specified file. * * @param filename The file to write to. * @return The newly opened stream. **/ public static ExceptionlessOutputStream openCompressedStream(String filename) { ExceptionlessOutputStream eos = null; try { ZipOutputStream zip = new ZipOutputStream( new FileOutputStream(filename)); zip.putNextEntry(new ZipEntry(ExceptionlessInputStream.zipEntryName)); eos = new ExceptionlessOutputStream( new BufferedOutputStream(zip)); } catch (Exception e) { System.err.println("Can't open '" + filename + "' for output:"); e.printStackTrace(); System.exit(1); } return eos; } /** * Opens a buffered (and uncompressed) stream for writing to the specified * file. If the specified URL does not reference a file on the local file * system, an error message will be displayed, and the program will exit. * * @param url The location of the file to write to. * @return The newly opened stream. **/ public static ExceptionlessOutputStream openBufferedStream(URL url) { if (!url.getProtocol().equals("file")) { System.err.println("Can't open URL with protocol '" + url.getProtocol() + "' for output."); new Exception().printStackTrace(); System.exit(1); } return openBufferedStream(url.getFile()); } /** * Opens a buffered stream for writing to the specified file. If the * specified URL does not reference a file on the local file system, an * error message will be displayed, and the program will exit. * * @param url The location of the file to write to. * @return The newly opened stream. **/ public static ExceptionlessOutputStream openCompressedStream(URL url) { if (!url.getProtocol().equals("file")) { System.err.println("Can't open URL with protocol '" + url.getProtocol() + "' for output."); new Exception().printStackTrace(); System.exit(1); } return openCompressedStream(url.getFile()); } /** * Creates a new data output stream to write data to the specified * underlying output stream. * * @param out The underlying output stream. **/ public ExceptionlessOutputStream(OutputStream out) { super(new DataOutputStream(out)); dos = (DataOutputStream) this.out; } /** * Whenever an exception is caught, this method attempts to close the * stream and exit the program. * * @param e The thrown exception. **/ private void handleException(Exception e) { System.err.println("Can't write to output stream:"); e.printStackTrace(); close(); System.exit(1); } /** * Closes this output stream and releases any system resources associated * with the stream. **/ public void close() { try { dos.close(); } catch (Exception e) { System.err.println("Can't close output stream:"); e.printStackTrace(); System.exit(1); } } /** * Writes a <code>boolean</code> to the underlying output stream as * a 1-byte value. The value <code>true</code> is written out as the * value <code>(byte)1</code>; the value <code>false</code> is * written out as the value <code>(byte)0</code>. * * @param v A <code>boolean</code> value to be written. **/ public void writeBoolean(boolean v) { try { dos.writeBoolean(v); } catch (Exception e) { handleException(e); } } /** * Writes out a <code>byte</code> to the underlying output stream as * a 1-byte value. * * @param v A <code>byte</code> value to be written. **/ public void writeByte(int v) { try { dos.writeByte(v); } catch (Exception e) { handleException(e); } } /** * Writes an array of bytes to the underlying output stream. First, an * integer representing the number of bytes in the array is written, * followed by the bytes in the array. * * @param ba The array of 8-bit bytes. **/ public void writeBytes(byte[] ba) { try { if (ba == null) { dos.writeInt(-1); return; } int n = ba.length; dos.writeInt(n); for (int i = 0; i < n; ++i) dos.writeByte(ba[i]); } catch (Exception e) { handleException(e); } } /** * Writes a <code>short</code> to the underlying output stream as two * bytes, high byte first. * * @param v A <code>short</code> to be written. **/ public void writeShort(int v) { try { dos.writeShort(v); } catch (Exception e) { handleException(e); } } /** * Writes a <code>char</code> to the underlying output stream as a * 2-byte value, high byte first. * * @param v A <code>char</code> value to be written. **/ public void writeChar(int v) { try { dos.writeChar(v); } catch (Exception e) { handleException(e); } } /** * Writes an <code>int</code> to the underlying output stream as four * bytes, high byte first. * * @param v An <code>int</code> to be written. **/ public void writeInt(int v) { try { dos.writeInt(v); } catch (Exception e) { handleException(e); } } /** * Writes a <code>long</code> to the underlying output stream as eight * bytes, high byte first. * * @param v A <code>long</code> to be written. */ public void writeLong(long v) { try { dos.writeLong(v); } catch (Exception e) { handleException(e); } } /** * Converts the float argument to an <code>int</code> using the * <code>floatToIntBits</code> method in class <code>Float</code>, * and then writes that <code>int</code> value to the underlying * output stream as a 4-byte quantity, high byte first. * * @param v A <code>float</code> value to be written. **/ public void writeFloat(float v) { try { dos.writeFloat(v); } catch (Exception e) { handleException(e); } } /** * Converts the double argument to a <code>long</code> using the * <code>doubleToLongBits</code> method in class <code>Double</code>, * and then writes that <code>long</code> value to the underlying * output stream as an 8-byte quantity, high byte first. * * @param v A <code>double</code> value to be written. **/ public void writeDouble(double v) { try { dos.writeDouble(v); } catch (Exception e) { handleException(e); } } /** * Writes a string to the underlying stream in such a way that it can be * read back in. In particular, the length of the string is written first. * * @param s The string to write. **/ public void writeString(String s) { if (s == null) writeShort((short) -1); else writeUTF(s); } /** * Writes a string using * <a href="http://java.sun.com/javase/6/docs/api/java/io/DataInput.html#modified-utf-8">modified UTF-8</a> * encoding in a machine-independent manner. * * <p> First, two bytes are written to out via the {@link #writeShort(int)} * method giving the number of bytes to follow. This value is the number * of bytes actually written out, not the length of the string. Following * the length, each character of the string is output, in sequence, using * the modified UTF-8 encoding for the character. * * @param str A string to be written. * @return The number of bytes written out. **/ public int writeUTF(String str) { int strlen = str.length(); int utfLength = 0; int c, count = 0; /* use charAt instead of copying String to char array */ for (int i = 0; i < strlen; i++) { c = str.charAt(i); if ((c >= 0x0001) && (c <= 0x007F)) utfLength++; else if (c > 0x07FF) utfLength += 3; else utfLength += 2; } if (utfLength > 32767) { System.err.println( "Error in ExceptionlessOutputStream: String too long"); new Exception().printStackTrace(); close(); System.exit(1); } if (buffer == null || buffer.length < utfLength) buffer = new byte[utfLength * 2]; writeShort((short) utfLength); int i = 0; for (i = 0; i < strlen; i++) { c = str.charAt(i); if (!((c >= 0x0001) && (c <= 0x007F))) break; buffer[count++] = (byte) c; } for (; i < strlen; i++) { c = str.charAt(i); if ((c >= 0x0001) && (c <= 0x007F)) buffer[count++] = (byte) c; else if (c > 0x07FF) { buffer[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); buffer[count++] = (byte) (0x80 | ((c >> 6) & 0x3F)); buffer[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); } else { buffer[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); buffer[count++] = (byte) (0x80 | ((c >> 0) & 0x3F)); } } try { dos.write(buffer, 0, utfLength); } catch (Exception e) { handleException(e); } return utfLength + 2; } }