0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.vfs;
0031:
0032: import com.caucho.util.CharSegment;
0033: import com.caucho.vfs.i18n.EncodingWriter;
0034:
0035: import java.io.IOException;
0036: import java.io.InputStream;
0037: import java.io.PrintWriter;
0038: import java.io.Reader;
0039: import java.io.UnsupportedEncodingException;
0040: import java.io.Writer;
0041: import java.util.Iterator;
0042: import java.util.Locale;
0043:
0044: /**
0045: * A fast bufferered output stream supporting both character
0046: * and byte data. The underlying stream sources are provided by StreamImpl
0047: * classes, so all streams have the same API regardless of the underlying
0048: * implementation.
0049: *
0050: * <p>OutputStream and Writers are combined. The <code>write</code> routines
0051: * write bytes and the <code>print</code> routines write characters.
0052: *
0053: * <p>Most applications will use the Path routines to create their own streams.
0054: * Specialized applications, like servers, need the capability of recycling
0055: * streams.
0056: */
0057: public class WriteStream extends OutputStreamWithBuffer implements
0058: LockableStream {
0059: private static byte[] lfBytes = new byte[] { '\n' };
0060: private static byte[] crBytes = new byte[] { '\r' };
0061: private static byte[] crlfBytes = new byte[] { '\r', '\n' };
0062:
0063: private static String _sysNewline;
0064: private static byte[] _sysNewlineBytes;
0065:
0066: static {
0067: _sysNewline = Path.getNewlineString();
0068: _sysNewlineBytes = _sysNewline.getBytes();
0069: }
0070:
0071: private TempBuffer _tempWrite;
0072: private byte[] _writeBuffer;
0073: private int _writeLength;
0074:
0075: private StreamImpl _source;
0076:
0077: private char[] chars;
0078: private byte[] _bytes;
0079:
0080: private EncodingWriter _writeEncoding;
0081: private String _writeEncodingName;
0082:
0083: private boolean _implicitFlush = false;
0084: private boolean flushOnNewline;
0085: private boolean _disableClose;
0086: private boolean _isDisableCloseSource;
0087: private boolean _disableFlush;
0088: private boolean reuseBuffer;
0089:
0090: private StreamWriter _writer;
0091: private StreamPrintWriter _printWriter;
0092:
0093: private String newline = "\n";
0094: private byte[] newlineBytes = lfBytes;
0095:
0096: /**
0097: * Creates an uninitialized stream. Use <code>init</code> to initialize.
0098: */
0099: public WriteStream() {
0100: }
0101:
0102: /**
0103: * Creates a stream and initializes with a specified source.
0104: *
0105: * @param source Underlying source for the stream.
0106: */
0107: public WriteStream(StreamImpl source) {
0108: init(source);
0109: }
0110:
0111: /**
0112: * Initializes the stream with a given source.
0113: *
0114: * @param source Underlying source for the stream.
0115: */
0116: public void init(StreamImpl source) {
0117: _disableClose = false;
0118: _isDisableCloseSource = false;
0119:
0120: if (this ._source != null && this ._source != source) {
0121: try {
0122: close();
0123: } catch (IOException e) {
0124: }
0125: }
0126:
0127: if (source == null)
0128: throw new IllegalArgumentException();
0129:
0130: if (_tempWrite == null) {
0131: _tempWrite = TempBuffer.allocate();
0132: _writeBuffer = _tempWrite._buf;
0133: }
0134:
0135: this ._source = source;
0136:
0137: _writeLength = 0;
0138: flushOnNewline = source.getFlushOnNewline();
0139: // Possibly, this should be dependent on the source. For example,
0140: // a http: stream should behave the same on Mac as on unix.
0141: // For now, CauchoSystem.getNewlineString() returns "\n".
0142: newline = _sysNewline;
0143: newlineBytes = _sysNewlineBytes;
0144:
0145: _writeEncoding = null;
0146: _writeEncodingName = "ISO-8859-1";
0147: }
0148:
0149: /**
0150: * Returns the underlying source for the stream.
0151: *
0152: * @return the source
0153: */
0154: public StreamImpl getSource() {
0155: return _source;
0156: }
0157:
0158: /**
0159: * Pushes a filter on the top of the stream stack.
0160: *
0161: * @param filter the filter to be added.
0162: */
0163: public void pushFilter(StreamFilter filter) {
0164: filter.init(_source);
0165: _source = filter;
0166: }
0167:
0168: /**
0169: * Returns true if the buffer allows writes.
0170: *
0171: * <p>LogStreams, used for debugging, use this feature to
0172: * test if they should write with very little overhead.
0173: *
0174: * <code><pre>
0175: * if (dbg.canWrite())
0176: * dbg.log("some debug value " + expensiveDebugCalculation(foo));
0177: * </pre></code>
0178: */
0179: public boolean canWrite() {
0180: return _source != null && _source.canWrite();
0181: }
0182:
0183: /**
0184: * Clears the write buffer
0185: */
0186: public void clearWrite() {
0187: if (_source != null)
0188: _source.clearWrite();
0189: }
0190:
0191: public void setReuseBuffer(boolean reuse) {
0192: this .reuseBuffer = reuse;
0193: }
0194:
0195: /**
0196: * Returns the write buffer.
0197: */
0198: public byte[] getBuffer() {
0199: return _writeBuffer;
0200: }
0201:
0202: /**
0203: * Returns the write offset.
0204: */
0205: public int getBufferOffset() {
0206: return _writeLength;
0207: }
0208:
0209: /**
0210: * Sets the write offset.
0211: */
0212: public void setBufferOffset(int offset) {
0213: _writeLength = offset;
0214: }
0215:
0216: /**
0217: * Returns the write size.
0218: */
0219: public int getBufferSize() {
0220: return _writeBuffer.length;
0221: }
0222:
0223: /**
0224: * Returns the bytes remaining in the buffer.
0225: */
0226: public int getRemaining() {
0227: if (_source == null)
0228: return 0;
0229: else
0230: return _writeBuffer.length - _writeLength;
0231: }
0232:
0233: public void setImplicitFlush(boolean implicitFlush) {
0234: _implicitFlush = implicitFlush;
0235: }
0236:
0237: /**
0238: * Writes a byte.
0239: */
0240: public void write(int ch) throws IOException {
0241: if (_source == null)
0242: return;
0243:
0244: int len = _writeLength;
0245: if (_writeBuffer.length <= len) {
0246: _writeLength = 0;
0247: _source.write(_writeBuffer, 0, len, false);
0248: }
0249:
0250: _writeBuffer[_writeLength++] = (byte) ch;
0251:
0252: if (_implicitFlush)
0253: flush();
0254: }
0255:
0256: /**
0257: * Writes a byte array
0258: */
0259: public void write(byte[] buf, int offset, int length)
0260: throws IOException {
0261: byte[] buffer = _writeBuffer;
0262:
0263: if (_source == null)
0264: return;
0265:
0266: int bufferLength = buffer.length;
0267: int writeLength = _writeLength;
0268:
0269: if (bufferLength <= length
0270: && _source.write(buffer, 0, writeLength, buf, offset,
0271: length, false)) {
0272: _writeLength = 0;
0273: return;
0274: }
0275:
0276: while (length > 0) {
0277: int sublen = bufferLength - writeLength;
0278:
0279: if (length < sublen)
0280: sublen = length;
0281:
0282: System.arraycopy(buf, offset, buffer, writeLength, sublen);
0283:
0284: writeLength += sublen;
0285: offset += sublen;
0286: length -= sublen;
0287:
0288: if (bufferLength <= writeLength) {
0289: int len = writeLength;
0290: writeLength = 0;
0291: _source.write(buffer, 0, len, false);
0292: }
0293: }
0294:
0295: _writeLength = writeLength;
0296:
0297: if (_implicitFlush)
0298: flush();
0299: }
0300:
0301: /**
0302: * Flushes and writes the buffer
0303: */
0304: public byte[] nextBuffer(int offset) throws IOException {
0305: _writeLength = 0;
0306: _source.write(_writeBuffer, 0, offset, false);
0307:
0308: if (_implicitFlush)
0309: flush();
0310:
0311: return _writeBuffer;
0312: }
0313:
0314: /**
0315: * Writes a byte array.
0316: */
0317: public void write(byte[] buf) throws IOException {
0318: write(buf, 0, buf.length);
0319: }
0320:
0321: /**
0322: * Flushes the buffer to the source.
0323: */
0324: public void flush() throws IOException {
0325: if (_disableFlush || _source == null)
0326: return;
0327:
0328: int len = _writeLength;
0329: if (len > 0) {
0330: _writeLength = 0;
0331: _source.write(_writeBuffer, 0, len, false);
0332: }
0333:
0334: if (_source != null)
0335: _source.flush();
0336: }
0337:
0338: /**
0339: * Flushes the buffer to the disk
0340: */
0341: public void flushToDisk() throws IOException {
0342: StreamImpl source = _source;
0343:
0344: if (_disableFlush || source == null)
0345: return;
0346:
0347: int len = _writeLength;
0348: if (len > 0) {
0349: _writeLength = 0;
0350: source.write(_writeBuffer, 0, len, false);
0351: }
0352:
0353: source.flushToDisk();
0354: }
0355:
0356: /**
0357: * Flushes the buffer to the source.
0358: */
0359: public void flushBuffer() throws IOException {
0360: if (_disableFlush || _source == null)
0361: return;
0362:
0363: int len = _writeLength;
0364: if (len > 0) {
0365: _writeLength = 0;
0366: _source.write(_writeBuffer, 0, len, false);
0367: _source.flushBuffer();
0368: }
0369: }
0370:
0371: /**
0372: * Seeks based on the start
0373: */
0374: public void seekStart(long offset) throws IOException {
0375: flushBuffer();
0376:
0377: StreamImpl source = _source;
0378:
0379: if (source != null)
0380: source.seekStart(offset);
0381: }
0382:
0383: /**
0384: * Seeks based on the end
0385: */
0386: public void seekEnd(long offset) throws IOException {
0387: flushBuffer();
0388:
0389: StreamImpl source = _source;
0390:
0391: if (source != null)
0392: source.seekEnd(offset);
0393: }
0394:
0395: /*
0396: * Writer methods
0397: */
0398:
0399: /**
0400: * Sets the character encoding for writing to this stream.
0401: * Encoding can be a Java encoding or a mime-encoding.
0402: */
0403: public void setEncoding(String encoding)
0404: throws UnsupportedEncodingException {
0405: if (_source instanceof ReaderWriterStream)
0406: encoding = "UTF-8";
0407:
0408: String mimeName = Encoding.getMimeName(encoding);
0409:
0410: if (mimeName != null && mimeName.equals(_writeEncodingName))
0411: return;
0412:
0413: if (_source != null)
0414: _source.setWriteEncoding(encoding);
0415:
0416: _writeEncoding = Encoding.getWriteEncoding(encoding);
0417: _writeEncodingName = mimeName;
0418: }
0419:
0420: public void setLocale(Locale locale)
0421: throws UnsupportedEncodingException {
0422: if (_writeEncoding == null && locale != null)
0423: setEncoding(Encoding.getMimeName(locale));
0424: }
0425:
0426: /**
0427: * Returns the mime-encoding used for writing.
0428: */
0429: public String getEncoding() {
0430: if (_source instanceof ReaderWriterStream)
0431: return ((ReaderWriterStream) _source).getEncoding();
0432: else
0433: return _writeEncodingName;
0434: }
0435:
0436: public String getJavaEncoding() {
0437: return Encoding.getJavaName(getEncoding());
0438: }
0439:
0440: /**
0441: * Some streams, like log streams, should be flushed on every println
0442: * call. Embedded newlines, i.e. '\n' in strings, do not trigger a
0443: * flush.
0444: *
0445: * @param flushOnNewline set to true if println flushes.
0446: */
0447: public void setFlushOnNewline(boolean flushOnNewline) {
0448: this .flushOnNewline = flushOnNewline;
0449: }
0450:
0451: /**
0452: * Returns the current string used for println newlines
0453: */
0454: public String getNewlineString() {
0455: return newline;
0456: }
0457:
0458: /**
0459: * Sets the string to use for println newlines
0460: */
0461: public void setNewlineString(String newline) {
0462: if (newline != null) {
0463: if (this .newline == newline || newline.equals(this .newline)) {
0464: } else if (newline == "\n" || newline.equals("\n")) {
0465: this .newlineBytes = lfBytes;
0466: } else if (newline == "\r\n" || newline.equals("\r\n")) {
0467: this .newlineBytes = crlfBytes;
0468: } else if (newline == "\r" || newline.equals("\r")) {
0469: this .newlineBytes = crBytes;
0470: } else {
0471: this .newlineBytes = newline.getBytes();
0472: }
0473: this .newline = newline;
0474: }
0475: }
0476:
0477: /**
0478: * Prints the character buffer to the stream.
0479: *
0480: * @param buffer character buffer to write
0481: * @param offset offset into the buffer to start writes
0482: * @param length number of characters to write
0483: */
0484: public final void print(char[] buffer, int offset, int length)
0485: throws IOException {
0486: byte[] writeBuffer = this ._writeBuffer;
0487:
0488: if (_source == null)
0489: return;
0490:
0491: if (_writeEncoding != null) {
0492: _disableFlush = true;
0493: _writeEncoding.write(this , buffer, offset, length);
0494: _disableFlush = false;
0495: return;
0496: }
0497:
0498: while (length > 0) {
0499: int writeLength = _writeLength;
0500: int sublen = writeBuffer.length - writeLength;
0501:
0502: if (sublen <= 0) {
0503: _source.write(writeBuffer, 0, writeLength, false);
0504: writeLength = 0;
0505: sublen = writeBuffer.length - writeLength;
0506: }
0507: if (length < sublen)
0508: sublen = length;
0509:
0510: for (int i = sublen - 1; i >= 0; i--)
0511: writeBuffer[writeLength + i] = (byte) buffer[offset + i];
0512:
0513: _writeLength = writeLength + sublen;
0514: offset += sublen;
0515: length -= sublen;
0516: }
0517:
0518: if (_implicitFlush)
0519: flush();
0520: }
0521:
0522: /**
0523: * Prints the character buffer to the stream.
0524: *
0525: * @param ch char
0526: */
0527: public final void print(char ch) throws IOException {
0528: if (_writeEncoding != null) {
0529: _disableFlush = true;
0530: _writeEncoding.write(this , ch);
0531: _disableFlush = false;
0532: return;
0533: }
0534:
0535: write((byte) ch);
0536: }
0537:
0538: /**
0539: * Prints the character buffer to the stream.
0540: *
0541: * @param buffer character buffer to write
0542: */
0543: public final void print(char[] buffer) throws IOException {
0544: print(buffer, 0, buffer.length);
0545: }
0546:
0547: /**
0548: * Prints the character buffer to the stream.
0549: *
0550: * @param segment character buffer to write
0551: */
0552: public final void print(CharSegment segment) throws IOException {
0553: print(segment.getBuffer(), segment.getOffset(), segment
0554: .getLength());
0555: }
0556:
0557: /**
0558: * Prints a string.
0559: */
0560: public final void print(String string) throws IOException {
0561: if (string == null)
0562: string = "null";
0563:
0564: int length = string.length();
0565: int offset = 0;
0566: while (length > 0) {
0567: int sublen = length < 1024 ? length : 1024;
0568:
0569: if (chars == null || chars.length < sublen)
0570: chars = new char[sublen < 32 ? 32 : sublen];
0571:
0572: string.getChars(offset, offset + sublen, chars, 0);
0573:
0574: print(chars, 0, sublen);
0575:
0576: length -= sublen;
0577: offset += sublen;
0578: }
0579: }
0580:
0581: /**
0582: * Prints a substring.
0583: *
0584: * @param string the string to print
0585: * @param offset starting offset into the string
0586: * @param length length of the substring to print.
0587: */
0588: public final void print(String string, int offset, int length)
0589: throws IOException {
0590: if (string == null)
0591: string = "null";
0592:
0593: while (length > 0) {
0594: int sublen = length < 1024 ? length : 1024;
0595:
0596: if (chars == null || chars.length < sublen)
0597: chars = new char[sublen < 32 ? 32 : sublen];
0598:
0599: string.getChars(offset, offset + sublen, chars, 0);
0600:
0601: print(chars, 0, sublen);
0602:
0603: length -= sublen;
0604: offset += sublen;
0605: }
0606: }
0607:
0608: /**
0609: * Prints a boolean.
0610: */
0611: public final void print(boolean b) throws IOException {
0612: print(b ? "true" : "false");
0613: }
0614:
0615: /**
0616: * Prints an integer.
0617: */
0618: public final void print(int i) throws IOException {
0619: if (i == 0x80000000) {
0620: print("-2147483648");
0621: return;
0622: }
0623:
0624: if (i < 0) {
0625: write('-');
0626: i = -i;
0627: } else if (i < 9) {
0628: write('0' + i);
0629: return;
0630: }
0631:
0632: int length = 0;
0633: int exp = 10;
0634:
0635: if (i >= 1000000000)
0636: length = 9;
0637: else {
0638: for (; i >= exp; length++)
0639: exp = 10 * exp;
0640: }
0641:
0642: byte[] buffer = _writeBuffer;
0643: int writeLength = this ._writeLength;
0644:
0645: if (writeLength + length < buffer.length) {
0646: writeLength += length;
0647: this ._writeLength = writeLength + 1;
0648:
0649: for (int j = 0; j <= length; j++) {
0650: buffer[writeLength - j] = (byte) (i % 10 + '0');
0651: i = i / 10;
0652: }
0653: return;
0654: }
0655:
0656: if (_bytes == null)
0657: _bytes = new byte[32];
0658:
0659: int j = 31;
0660:
0661: while (i > 0) {
0662: _bytes[--j] = (byte) ((i % 10) + '0');
0663: i /= 10;
0664: }
0665:
0666: write(_bytes, j, 31 - j);
0667: }
0668:
0669: /**
0670: * Prints a long.
0671: */
0672: public final void print(long i) throws IOException {
0673: if (i == 0x8000000000000000L) {
0674: print("-9223372036854775808");
0675: return;
0676: }
0677:
0678: if (_bytes == null)
0679: _bytes = new byte[32];
0680:
0681: if (i < 0) {
0682: write('-');
0683: i = -i;
0684: } else if (i == 0) {
0685: write('0');
0686: return;
0687: }
0688:
0689: int j = 31;
0690:
0691: while (i > 0) {
0692: _bytes[--j] = (byte) ((i % 10) + '0');
0693: i /= 10;
0694: }
0695:
0696: write(_bytes, j, 31 - j);
0697: }
0698:
0699: /**
0700: * Prints a float.
0701: */
0702: public final void print(float f) throws IOException {
0703: print(String.valueOf(f));
0704: }
0705:
0706: /**
0707: * Prints an double
0708: */
0709: public final void print(double d) throws IOException {
0710: print(String.valueOf(d));
0711: }
0712:
0713: /**
0714: * Prints a double, converted by String.valueOf()
0715: */
0716: public final void print(Object o) throws IOException {
0717: if (o == null)
0718: print("null");
0719: else if (o instanceof VfsWriteObject)
0720: ((VfsWriteObject) o).print(this );
0721: else
0722: print(o.toString());
0723: }
0724:
0725: /**
0726: * Prints a newline
0727: */
0728: public final void println() throws IOException {
0729: write(newlineBytes, 0, newlineBytes.length);
0730: if (flushOnNewline)
0731: flush();
0732: }
0733:
0734: /**
0735: * Prints a character buffer followed by a newline.
0736: */
0737: public final void println(char[] buf, int offset, int length)
0738: throws IOException {
0739: print(buf, offset, length);
0740: write(newlineBytes, 0, newlineBytes.length);
0741: if (flushOnNewline)
0742: flush();
0743: }
0744:
0745: /**
0746: * Prints a string buffer followed by a newline.
0747: */
0748: public final void println(String string) throws IOException {
0749: print(string);
0750: write(newlineBytes, 0, newlineBytes.length);
0751: if (flushOnNewline)
0752: flush();
0753: }
0754:
0755: /**
0756: * Prints a boolean followed by a newline.
0757: */
0758: public final void println(boolean b) throws IOException {
0759: println(b ? "true" : "false");
0760: }
0761:
0762: /**
0763: * Prints a char followed by a newline.
0764: */
0765: public final void println(char ch) throws IOException {
0766: write(ch);
0767: write(newlineBytes, 0, newlineBytes.length);
0768: if (flushOnNewline)
0769: flush();
0770: }
0771:
0772: /**
0773: * Prints an integer followed by a newline.
0774: */
0775: public final void println(int i) throws IOException {
0776: print(i);
0777: write(newlineBytes, 0, newlineBytes.length);
0778: if (flushOnNewline)
0779: flush();
0780: }
0781:
0782: /**
0783: * Prints a long followed by a newline.
0784: */
0785: public final void println(long l) throws IOException {
0786: print(l);
0787: write(newlineBytes, 0, newlineBytes.length);
0788: if (flushOnNewline)
0789: flush();
0790: }
0791:
0792: /**
0793: * Prints a float followed by a newline.
0794: */
0795: public final void println(float f) throws IOException {
0796: println(String.valueOf(f));
0797: }
0798:
0799: /**
0800: * Prints a double followed by a newline.
0801: */
0802: public final void println(double d) throws IOException {
0803: println(String.valueOf(d));
0804: }
0805:
0806: /**
0807: * Prints an object, converted to a string, followed by a newline.
0808: */
0809: public final void println(Object o) throws IOException {
0810: if (o == null)
0811: println("null");
0812: else
0813: println(o.toString());
0814: }
0815:
0816: /**
0817: * Returns a printWriter writing to this stream.
0818: */
0819: public PrintWriter getPrintWriter() {
0820: /*
0821: if (_writer == null)
0822: _writer = new StreamWriter();
0823: */
0824:
0825: if (_printWriter == null)
0826: _printWriter = new StreamPrintWriter(this );
0827: /*
0828: else
0829: _printWriter.setWriter(_writer);
0830: */
0831:
0832: return _printWriter;
0833: }
0834:
0835: /**
0836: * Logs a line to the stream. log is essentially println, but
0837: * it doesn't throw an exception and it always flushes the output.
0838: */
0839: public final void log(String string) {
0840: try {
0841: synchronized (this ) {
0842: println(string);
0843: flush();
0844: }
0845: } catch (Exception e) {
0846: }
0847: }
0848:
0849: public final void log(Throwable exn) {
0850: try {
0851: PrintWriter out = getPrintWriter();
0852: synchronized (this ) {
0853: if (exn != null) {
0854: exn.printStackTrace(out);
0855: flush();
0856: }
0857: }
0858: } catch (Throwable e) {
0859: e.printStackTrace();
0860: }
0861: }
0862:
0863: /**
0864: * Writes the contents of a JDK stream. Essentially this will copy
0865: * <code>source</code> to the current stream.
0866: *
0867: * @param source InputStream to read.
0868: */
0869: public long writeStream(InputStream source) throws IOException {
0870: if (source == null)
0871: return 0;
0872:
0873: int len;
0874: int length = _writeBuffer.length;
0875: long outputLength = 0;
0876:
0877: if (_writeLength >= length) {
0878: int tmplen = _writeLength;
0879: _writeLength = 0;
0880: _source.write(_writeBuffer, 0, tmplen, false);
0881: outputLength += tmplen;
0882: }
0883:
0884: while ((len = source.read(_writeBuffer, _writeLength, length
0885: - _writeLength)) >= 0) {
0886: _writeLength += len;
0887: outputLength += len;
0888:
0889: if (length <= _writeLength) {
0890: int tmplen = _writeLength;
0891: _writeLength = 0;
0892: _source.write(_writeBuffer, 0, tmplen, false);
0893: }
0894: }
0895:
0896: if (flushOnNewline || _implicitFlush)
0897: flush();
0898:
0899: return outputLength;
0900: }
0901:
0902: /**
0903: * Writes the contents of a JDK reader. Essentially this will copy
0904: * <code>source</code> to the current stream.
0905: *
0906: * @param source InputStream to read.
0907: */
0908: public void writeStream(Reader reader) throws IOException {
0909: if (reader == null)
0910: return;
0911:
0912: if (chars == null)
0913: chars = new char[256];
0914:
0915: int len;
0916: while ((len = reader.read(chars, 0, chars.length)) > 0) {
0917: print(chars, 0, len);
0918: }
0919: }
0920:
0921: /**
0922: * Writes the contents of a JDK stream. Essentially this will copy
0923: * <code>source</code> to the current stream.
0924: *
0925: * @param source InputStream to read.
0926: */
0927: public void writeStream(InputStream source, int totalLength)
0928: throws IOException {
0929: if (source == null)
0930: return;
0931:
0932: int len;
0933: int length = _writeBuffer.length;
0934:
0935: if (length <= _writeLength) {
0936: int tmplen = _writeLength;
0937: _writeLength = 0;
0938: _source.write(_writeBuffer, 0, tmplen, false);
0939: }
0940:
0941: while (totalLength > 0) {
0942: int sublen = length - _writeLength;
0943:
0944: if (totalLength < sublen)
0945: sublen = totalLength;
0946:
0947: sublen = source.read(_writeBuffer, _writeLength, sublen);
0948: if (sublen < 0)
0949: break;
0950:
0951: _writeLength += sublen;
0952: totalLength -= sublen;
0953: if (length <= _writeLength) {
0954: int tmplen = _writeLength;
0955: _writeLength = 0;
0956: _source.write(_writeBuffer, 0, tmplen, false);
0957: }
0958: }
0959:
0960: if (flushOnNewline || _implicitFlush)
0961: flush();
0962: }
0963:
0964: /**
0965: * Writes the contents of a JDK stream. Essentially this will copy
0966: * <code>source</code> to the current stream.
0967: *
0968: * @param source InputStream to read.
0969: */
0970: public void writeStream(StreamImpl source) throws IOException {
0971: if (source == null)
0972: return;
0973:
0974: int len;
0975: int length = _writeBuffer.length;
0976:
0977: if (_writeLength >= length) {
0978: int tmplen = _writeLength;
0979: _writeLength = 0;
0980: this ._source.write(_writeBuffer, 0, tmplen, false);
0981: }
0982:
0983: while ((len = source.read(_writeBuffer, _writeLength, length
0984: - _writeLength)) >= 0) {
0985: _writeLength += len;
0986: if (_writeLength >= length) {
0987: int tmplen = _writeLength;
0988: _writeLength = 0;
0989: this ._source.write(_writeBuffer, 0, tmplen, false);
0990: }
0991: }
0992:
0993: if (flushOnNewline || _implicitFlush)
0994: flush();
0995: }
0996:
0997: /**
0998: * Copies a file to the stream.
0999: *
1000: * @param path Path of the file to copy.
1001: */
1002: public void writeFile(Path path) throws IOException {
1003: StreamImpl is = path.openReadImpl();
1004:
1005: try {
1006: if (is != null)
1007: writeStream(is);
1008: } finally {
1009: if (is != null)
1010: is.close();
1011: }
1012: }
1013:
1014: /**
1015: * Disables close. Sometimes an application will pass a stream
1016: * to a client that may close the stream at an inappropriate time.
1017: * Setting disable close gives the calling routine control over closing
1018: * the stream.
1019: */
1020: public void setDisableClose(boolean disableClose) {
1021: this ._disableClose = disableClose;
1022: }
1023:
1024: public boolean getDisableClose() {
1025: return _disableClose;
1026: }
1027:
1028: /**
1029: * Disables close of the underlying source.
1030: */
1031: public void setDisableCloseSource(boolean disableClose) {
1032: _isDisableCloseSource = disableClose;
1033: }
1034:
1035: /**
1036: * Returns true if the stream is closed.
1037: */
1038: public final boolean isClosed() {
1039: return _source != null;
1040: }
1041:
1042: /**
1043: * Close the stream, first flushing the write buffer.
1044: */
1045: public final void close() throws IOException {
1046: StreamImpl s = _source;
1047:
1048: try {
1049: int len = _writeLength;
1050: if (len > 0) {
1051: _writeLength = 0;
1052: if (s != null)
1053: s.write(_writeBuffer, 0, len, true);
1054: }
1055: } finally {
1056: if (_disableClose) {
1057: return;
1058: }
1059:
1060: _source = null;
1061:
1062: if (_writeEncoding != null)
1063: _writeEncoding = null;
1064:
1065: if (!reuseBuffer) {
1066: if (_tempWrite != null) {
1067: TempBuffer.free(_tempWrite);
1068: _tempWrite = null;
1069: }
1070: _tempWrite = null;
1071: _writeBuffer = null;
1072: }
1073:
1074: if (s != null && !_isDisableCloseSource)
1075: s.closeWrite();
1076: }
1077: }
1078:
1079: /**
1080: * Frees the buffer
1081: */
1082: public final void free() {
1083: _source = null;
1084:
1085: if (_tempWrite != null) {
1086: TempBuffer.free(_tempWrite);
1087: _tempWrite = null;
1088: }
1089:
1090: _tempWrite = null;
1091: _writeBuffer = null;
1092: }
1093:
1094: /**
1095: * Returns a named attribute. For example, an HTTP stream
1096: * may use this to return header values.
1097: */
1098: public Object getAttribute(String name) throws IOException {
1099: return _source.getAttribute(name);
1100: }
1101:
1102: /**
1103: * Sets a named attribute. For example, an HTTP stream
1104: * may use this to set header values.
1105: */
1106: public void setAttribute(String name, Object value)
1107: throws IOException {
1108: _source.setAttribute(name, value);
1109: }
1110:
1111: /**
1112: * Removes a named attribute.
1113: */
1114: public void removeAttribute(String name) throws IOException {
1115: _source.removeAttribute(name);
1116: }
1117:
1118: /**
1119: * Lists all named attributes.
1120: */
1121: public Iterator getAttributeNames() throws IOException {
1122: return _source.getAttributeNames();
1123: }
1124:
1125: /**
1126: * Returns the Path which opened this stream.
1127: */
1128: public Path getPath() {
1129: if (_source != null)
1130: return _source.getPath();
1131: else
1132: return null;
1133: }
1134:
1135: /**
1136: * Returns the user path which opened this stream.
1137: *
1138: * <p>Parsing routines typically use this for error reporting.
1139: */
1140: public String getUserPath() {
1141: return _source.getPath().getUserPath();
1142: }
1143:
1144: /**
1145: * Sets a path name associated with the stream.
1146: */
1147: public void setPath(Path path) {
1148: _source.setPath(path);
1149: }
1150:
1151: /**
1152: * For testing, sets the system newlines.
1153: */
1154: public static void setSystemNewline(String newline) {
1155: _sysNewline = newline;
1156: _sysNewlineBytes = _sysNewline.getBytes();
1157: }
1158:
1159: public boolean lock(boolean shared, boolean block) {
1160: if (!(_source instanceof LockableStream))
1161: return true;
1162:
1163: LockableStream ls = (LockableStream) _source;
1164: return ls.lock(shared, block);
1165: }
1166:
1167: public boolean unlock() {
1168: if (!(_source instanceof LockableStream))
1169: return true;
1170:
1171: LockableStream ls = (LockableStream) _source;
1172: return ls.unlock();
1173: }
1174:
1175: private class StreamWriter extends Writer implements
1176: EnclosedWriteStream, FlushBuffer {
1177: public final void write(char ch) throws IOException {
1178: WriteStream.this .print(ch);
1179: }
1180:
1181: public final void write(char[] buffer, int offset, int length)
1182: throws IOException {
1183: WriteStream.this .print(buffer, offset, length);
1184: }
1185:
1186: public final void write(char[] buffer) throws IOException {
1187: WriteStream.this .print(buffer, 0, buffer.length);
1188: }
1189:
1190: public final void write(String string) throws IOException {
1191: WriteStream.this .print(string);
1192: }
1193:
1194: public final void write(String string, int off, int len)
1195: throws IOException {
1196: WriteStream.this .print(string, off, len);
1197: }
1198:
1199: public final void flush() throws IOException {
1200: WriteStream.this .flush();
1201: }
1202:
1203: public final void flushBuffer() throws IOException {
1204: WriteStream.this .flushBuffer();
1205: }
1206:
1207: public final void close() throws IOException {
1208: // XXX: if flush, then servlets are sloooow
1209: // WriteStream.this.flush();
1210: }
1211:
1212: public WriteStream getWriteStream() {
1213: return WriteStream.this;
1214: }
1215: }
1216: }
|