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: * Free SoftwareFoundation, Inc.
0023: * 59 Temple Place, Suite 330
0024: * Boston, MA 02111-1307 USA
0025: *
0026: * @author Scott Ferguson
0027: */
0028:
0029: package com.caucho.vfs;
0030:
0031: import com.caucho.util.Alarm;
0032: import com.caucho.util.CharBuffer;
0033:
0034: import java.io.IOException;
0035: import java.io.InputStream;
0036: import java.io.OutputStream;
0037: import java.io.Reader;
0038: import java.io.UnsupportedEncodingException;
0039: import java.io.Writer;
0040: import java.util.Iterator;
0041: import java.util.logging.Level;
0042: import java.util.logging.Logger;
0043:
0044: /**
0045: * A fast bufferered input 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>Dynamic streams, like tcp and http
0051: * will properly flush writes before reading input. And random access
0052: * streams, like RandomAccessFile, can use the same API as normal streams.
0053: *
0054: * <p>Most applications will use the Path routines to create their own streams.
0055: * Specialized applications, like servers, need the capability of recycling
0056: * streams.
0057: */
0058: public final class ReadStream extends InputStream implements
0059: LockableStream {
0060: public static int ZERO_COPY_SIZE = 1024;
0061:
0062: private TempBuffer _tempRead;
0063: private byte[] _readBuffer;
0064: private int _readOffset;
0065: private int _readLength;
0066:
0067: private WriteStream _sibling;
0068:
0069: private StreamImpl _source;
0070: private long _position;
0071:
0072: private long _readTime;
0073:
0074: private Reader _readEncoding;
0075: private String _readEncodingName;
0076: private int _specialEncoding;
0077:
0078: private boolean _disableClose;
0079: private boolean _isDisableCloseSource;
0080: private boolean _reuseBuffer;
0081: private Reader _reader;
0082:
0083: /**
0084: * Creates an uninitialized stream. Use <code>init</code> to initialize.
0085: */
0086: public ReadStream() {
0087: }
0088:
0089: /**
0090: * Creates a stream and initializes with a specified source.
0091: *
0092: * @param source Underlying source for the stream.
0093: */
0094: public ReadStream(StreamImpl source) {
0095: init(source, null);
0096: }
0097:
0098: /**
0099: * Creates a stream and initializes with a specified source.
0100: *
0101: * @param source Underlying source for the stream.
0102: * @param sibling Sibling write stream.
0103: */
0104: public ReadStream(StreamImpl source, WriteStream sibling) {
0105: init(source, sibling);
0106: }
0107:
0108: /**
0109: * Initializes the stream with a given source.
0110: *
0111: * @param source Underlying source for the stream.
0112: * @param sibling Sibling write stream
0113: */
0114: public void init(StreamImpl source, WriteStream sibling) {
0115: _disableClose = false;
0116: _isDisableCloseSource = false;
0117: _readTime = 0;
0118:
0119: if (_source != null && _source != source) {
0120: close();
0121: }
0122:
0123: if (source == null)
0124: throw new IllegalArgumentException();
0125:
0126: _source = source;
0127: _sibling = sibling;
0128:
0129: if (source.canRead()) {
0130: if (_tempRead == null) {
0131: _tempRead = TempBuffer.allocate();
0132: _readBuffer = _tempRead._buf;
0133: }
0134: }
0135: _readOffset = 0;
0136: _readLength = 0;
0137:
0138: _readEncoding = null;
0139: _readEncodingName = "ISO-8859-1";
0140: }
0141:
0142: public void setSibling(WriteStream sibling) {
0143: _sibling = sibling;
0144: }
0145:
0146: public WriteStream getSibling() {
0147: return _sibling;
0148: }
0149:
0150: /**
0151: * Returns the underlying source for the stream.
0152: *
0153: * @return the source
0154: */
0155: public StreamImpl getSource() {
0156: return _source;
0157: }
0158:
0159: public void setReuseBuffer(boolean reuse) {
0160: _reuseBuffer = reuse;
0161: }
0162:
0163: /**
0164: * Pushes a filter on the top of the stream stack.
0165: *
0166: * @param filter the filter to be added.
0167: */
0168: public void pushFilter(StreamFilter filter) {
0169: filter.init(_source);
0170: _source = filter;
0171: }
0172:
0173: public byte[] getBuffer() {
0174: return _readBuffer;
0175: }
0176:
0177: public int getOffset() {
0178: return _readOffset;
0179: }
0180:
0181: public int getLength() {
0182: return _readLength;
0183: }
0184:
0185: public void setOffset(int offset) {
0186: _readOffset = offset;
0187: }
0188:
0189: /**
0190: * Returns the read position.
0191: */
0192: public long getPosition() {
0193: return _position - (_readLength - _readOffset);
0194: }
0195:
0196: /**
0197: * Returns the last read-time.
0198: */
0199: public long getReadTime() {
0200: return _readTime;
0201: }
0202:
0203: /**
0204: * Returns the sets current read position.
0205: */
0206: public boolean setPosition(long pos) throws IOException {
0207: // Allow seeks to be reflected in the char Reader.
0208: if (_readEncoding != null)
0209: _readEncoding = Encoding.getReadEncoding(this ,
0210: _readEncodingName);
0211:
0212: if (pos < 0) {
0213: // Return error on seek to negative stream position
0214:
0215: return false;
0216: } else if (pos < getPosition()) {
0217: // Seek backwards in the stream
0218:
0219: _position = pos;
0220: _readLength = _readOffset = 0;
0221:
0222: if (_source != null) {
0223: _source.seekStart(pos);
0224:
0225: return true;
0226: } else
0227: return false;
0228: } else {
0229: // Seek forward in the stream, skip any buffered bytes
0230:
0231: long n = pos - getPosition();
0232:
0233: return skip(n) == n;
0234: }
0235: }
0236:
0237: /**
0238: * Returns true if the stream allows reading.
0239: */
0240: public boolean canRead() {
0241: return _source.canRead();
0242: }
0243:
0244: /**
0245: * Clears the read buffer.
0246: */
0247: public void clearRead() {
0248: _readOffset = 0;
0249: _readLength = 0;
0250: }
0251:
0252: /**
0253: * Returns an estimate of the available bytes. If a read would not block,
0254: * it will always return greater than 0.
0255: */
0256: public int getAvailable() throws IOException {
0257: if (_readOffset < _readLength) {
0258: return _readLength - _readOffset;
0259: }
0260:
0261: if (_sibling != null)
0262: _sibling.flush();
0263:
0264: return _source.getAvailable();
0265: }
0266:
0267: /**
0268: * Returns true if data in the buffer is available.
0269: */
0270: public int getBufferAvailable() throws IOException {
0271: return _readLength - _readOffset;
0272: }
0273:
0274: /**
0275: * Compatibility with InputStream.
0276: */
0277: @Override
0278: public int available() throws IOException {
0279: return getAvailable();
0280: }
0281:
0282: /**
0283: * Returns the next byte or -1 if at the end of file.
0284: */
0285: public final int read() throws IOException {
0286: if (_readLength <= _readOffset) {
0287: if (!readBuffer())
0288: return -1;
0289: }
0290:
0291: return _readBuffer[_readOffset++] & 0xff;
0292: }
0293:
0294: /**
0295: * Unreads the last byte.
0296: */
0297: public final void unread() {
0298: if (_readOffset <= 0)
0299: throw new RuntimeException();
0300:
0301: _readOffset--;
0302: }
0303:
0304: /**
0305: * Waits for data to be available.
0306: */
0307: public final boolean waitForRead() throws IOException {
0308: if (_readLength <= _readOffset) {
0309: if (!readBuffer())
0310: return false;
0311: }
0312:
0313: return true;
0314: }
0315:
0316: /**
0317: * Skips the next <code>n</code> bytes.
0318: *
0319: * @param n bytes to skip.
0320: *
0321: * @return number of bytes skipped.
0322: */
0323: @Override
0324: public long skip(long n) throws IOException {
0325: long buffered = getBufferAvailable();
0326:
0327: if (n < buffered) {
0328: _readOffset += n;
0329: return n;
0330: }
0331:
0332: _readLength = 0;
0333: _readOffset = 0;
0334:
0335: if (_source.hasSkip()) {
0336: if (_sibling != null)
0337: _sibling.flush();
0338:
0339: long skipped = _source.skip(n - buffered);
0340:
0341: if (skipped < 0)
0342: return buffered;
0343: else {
0344: _position += skipped + buffered;
0345: return skipped + buffered;
0346: }
0347: }
0348:
0349: while (_readLength < (_readOffset + n - buffered)) {
0350: buffered += getBufferAvailable();
0351: _readOffset = 0;
0352: _readLength = 0;
0353:
0354: if (!readBuffer())
0355: return buffered;
0356: }
0357:
0358: _readOffset += (int) (n - buffered);
0359:
0360: return n;
0361: }
0362:
0363: /**
0364: * Reads into a byte array. <code>read</code> may return less than
0365: * the maximum bytes even if more bytes are available to read.
0366: *
0367: * @param buf byte array
0368: * @param offset offset into the byte array to start reading
0369: * @param length maximum byte allowed to read.
0370: *
0371: * @return number of bytes read or -1 on end of file.
0372: */
0373: @Override
0374: public final int read(byte[] buf, int offset, int length)
0375: throws IOException {
0376: int readOffset = _readOffset;
0377: int readLength = _readLength;
0378:
0379: if (readLength <= readOffset) {
0380: if (ZERO_COPY_SIZE <= length) {
0381: if (_sibling != null)
0382: _sibling.flush();
0383:
0384: int len = _source.read(buf, offset, length);
0385:
0386: if (len > 0) {
0387: _position += len;
0388: _readTime = Alarm.getCurrentTime();
0389: }
0390:
0391: return len;
0392: }
0393:
0394: if (!readBuffer())
0395: return -1;
0396:
0397: readOffset = _readOffset;
0398: readLength = _readLength;
0399: }
0400:
0401: int sublen = readLength - readOffset;
0402: if (length < sublen)
0403: sublen = length;
0404:
0405: System.arraycopy(_readBuffer, readOffset, buf, offset, sublen);
0406:
0407: _readOffset = readOffset + sublen;
0408:
0409: return sublen;
0410: }
0411:
0412: /**
0413: * Reads into a byte array. <code>readAll</code> will always read
0414: * <code>length</code> bytes, blocking if necessary, until the end of
0415: * file is reached.
0416: *
0417: * @param buf byte array
0418: * @param offset offset into the byte array to start reading
0419: * @param length maximum byte allowed to read.
0420: *
0421: * @return number of bytes read or -1 on end of file.
0422: */
0423: public int readAll(byte[] buf, int offset, int length)
0424: throws IOException {
0425: int readLength = 0;
0426:
0427: while (length > 0) {
0428: int sublen = read(buf, offset, length);
0429:
0430: if (sublen < 0)
0431: return readLength == 0 ? -1 : readLength;
0432:
0433: offset += sublen;
0434: readLength += sublen;
0435: length -= sublen;
0436: }
0437:
0438: return readLength == 0 ? -1 : readLength;
0439: }
0440:
0441: /*
0442: * Reader methods
0443: */
0444:
0445: /**
0446: * Sets the current read encoding. The encoding can either be a
0447: * Java encoding name or a mime encoding.
0448: *
0449: * @param encoding name of the read encoding
0450: */
0451: public void setEncoding(String encoding)
0452: throws UnsupportedEncodingException {
0453: String mimeName = Encoding.getMimeName(encoding);
0454:
0455: if (mimeName != null && mimeName.equals(_readEncodingName))
0456: return;
0457:
0458: _readEncoding = Encoding.getReadEncoding(this , encoding);
0459: _readEncodingName = mimeName;
0460: }
0461:
0462: /**
0463: * Returns the mime-encoding currently read.
0464: */
0465: public String getEncoding() {
0466: return _readEncodingName;
0467: }
0468:
0469: /**
0470: * Reads a character from the stream, returning -1 on end of file.
0471: */
0472: public final int readChar() throws IOException {
0473: if (_readEncoding != null) {
0474: int ch = _readEncoding.read();
0475: return ch;
0476: }
0477:
0478: if (_readLength <= _readOffset) {
0479: if (!readBuffer())
0480: return -1;
0481: }
0482:
0483: return _readBuffer[_readOffset++] & 0xff;
0484: }
0485:
0486: /**
0487: * Reads into a character buffer from the stream. Like the byte
0488: * array version, read may return less characters even though more
0489: * characters are available.
0490: *
0491: * @param buf character buffer to fill
0492: * @param offset starting offset into the character buffer
0493: * @param length maximum number of characters to read
0494: * @return number of characters read or -1 on end of file.
0495: */
0496: public final int read(char[] buf, int offset, int length)
0497: throws IOException {
0498: if (_readEncoding != null)
0499: return _readEncoding.read(buf, offset, length);
0500:
0501: byte[] readBuffer = _readBuffer;
0502: if (readBuffer == null)
0503: return -1;
0504:
0505: int readOffset = _readOffset;
0506: int readLength = _readLength;
0507:
0508: int sublen = readLength - readOffset;
0509:
0510: if (sublen <= 0) {
0511: if (!readBuffer()) {
0512: return -1;
0513: }
0514: readLength = _readLength;
0515: readOffset = _readOffset;
0516: sublen = readLength - readOffset;
0517: }
0518:
0519: if (length < sublen)
0520: sublen = length;
0521:
0522: for (int i = 0; i < sublen; i++)
0523: buf[offset + i] = (char) (readBuffer[readOffset + i] & 0xff);
0524:
0525: _readOffset = readOffset + sublen;
0526:
0527: return sublen;
0528: }
0529:
0530: /**
0531: * Reads into a character buffer from the stream. <code>length</code>
0532: * characters will always be read until the end of file is reached.
0533: *
0534: * @param buf character buffer to fill
0535: * @param offset starting offset into the character buffer
0536: * @param length maximum number of characters to read
0537: * @return number of characters read or -1 on end of file.
0538: */
0539: public int readAll(char[] buf, int offset, int length)
0540: throws IOException {
0541: int readLength = 0;
0542:
0543: while (length > 0) {
0544: int sublen = read(buf, offset, length);
0545:
0546: if (sublen <= 0)
0547: return readLength > 0 ? readLength : -1;
0548:
0549: offset += sublen;
0550: readLength += sublen;
0551: length -= sublen;
0552: }
0553:
0554: return readLength;
0555: }
0556:
0557: /**
0558: * Reads characters from the stream, appending to the character buffer.
0559: *
0560: * @param buf character buffer to fill
0561: * @param length maximum number of characters to read
0562: * @return number of characters read or -1 on end of file.
0563: */
0564: public int read(CharBuffer buf, int length) throws IOException {
0565: int len = buf.getLength();
0566:
0567: buf.setLength(len + length);
0568: int readLength = read(buf.getBuffer(), len, length);
0569: if (readLength < 0)
0570: buf.setLength(len);
0571: else if (readLength < length)
0572: buf.setLength(len + readLength);
0573:
0574: return length;
0575: }
0576:
0577: /**
0578: * Reads characters from the stream, appending to the character buffer.
0579: * <code>length</code> characters will always be read until the end of
0580: * file.
0581: *
0582: * @param buf character buffer to fill
0583: * @param length maximum number of characters to read
0584: * @return number of characters read or -1 on end of file.
0585: */
0586: public int readAll(CharBuffer buf, int length) throws IOException {
0587: int len = buf.getLength();
0588:
0589: buf.setLength(len + length);
0590: int readLength = readAll(buf.getBuffer(), len, length);
0591: if (readLength < 0)
0592: buf.setLength(len);
0593: else if (readLength < length)
0594: buf.setLength(len + readLength);
0595:
0596: return length;
0597: }
0598:
0599: /**
0600: * Reads a line from the stream, returning a string.
0601: */
0602: public final String readln() throws IOException {
0603: return readLine();
0604: }
0605:
0606: /**
0607: * Reads a line, returning a string.
0608: */
0609: public String readLine() throws IOException {
0610: CharBuffer cb = new CharBuffer();
0611:
0612: if (readLine(cb, true))
0613: return cb.toString();
0614: else if (cb.length() == 0)
0615: return null;
0616: else
0617: return cb.toString();
0618: }
0619:
0620: /**
0621: * Reads a line, returning a string.
0622: */
0623: public String readLineNoChop() throws IOException {
0624: CharBuffer cb = new CharBuffer();
0625:
0626: if (readLine(cb, false))
0627: return cb.toString();
0628: else if (cb.length() == 0)
0629: return null;
0630: else
0631: return cb.toString();
0632: }
0633:
0634: /**
0635: * Fills the buffer with the next line from the input stream.
0636: *
0637: * @return true on success, false on end of file.
0638: */
0639: public final boolean readln(CharBuffer cb) throws IOException {
0640: return readLine(cb, true);
0641: }
0642:
0643: /**
0644: * Reads a line into the character buffer. \r\n is converted to \n.
0645: *
0646: * @param buf character buffer to fill
0647: * @return false on end of file
0648: */
0649: public final boolean readLine(CharBuffer cb) throws IOException {
0650: return readLine(cb, true);
0651: }
0652:
0653: /**
0654: * Reads a line into the character buffer. \r\n is converted to \n.
0655: *
0656: * @param buf character buffer to fill
0657: * @return false on end of file
0658: */
0659: public final boolean readLine(CharBuffer cb, boolean isChop)
0660: throws IOException {
0661: if (_readEncoding != null)
0662: return readlnEncoded(cb, isChop);
0663:
0664: int capacity = cb.getCapacity();
0665: int offset = cb.getLength();
0666: char[] buf = cb.getBuffer();
0667:
0668: byte[] readBuffer = _readBuffer;
0669:
0670: while (true) {
0671: int readOffset = _readOffset;
0672:
0673: int sublen = _readLength - readOffset;
0674: if (capacity - offset < sublen)
0675: sublen = capacity - offset;
0676:
0677: for (; sublen > 0; sublen--) {
0678: int ch = readBuffer[readOffset++] & 0xff;
0679:
0680: if (ch != '\n') {
0681: buf[offset++] = (char) ch;
0682: } else if (isChop) {
0683: if (offset > 0 && buf[offset - 1] == '\r')
0684: cb.setLength(offset - 1);
0685: else
0686: cb.setLength(offset);
0687:
0688: _readOffset = readOffset;
0689:
0690: return true;
0691: } else {
0692: buf[offset++] = (char) '\n';
0693:
0694: cb.setLength(offset);
0695:
0696: _readOffset = readOffset;
0697:
0698: return true;
0699: }
0700: }
0701:
0702: _readOffset = readOffset;
0703:
0704: if (_readLength <= readOffset) {
0705: if (!readBuffer()) {
0706: cb.setLength(offset);
0707: return offset > 0;
0708: }
0709: }
0710:
0711: if (capacity <= offset) {
0712: cb.setLength(offset + 1);
0713: capacity = cb.getCapacity();
0714: buf = cb.getBuffer();
0715: }
0716: }
0717: }
0718:
0719: /**
0720: * Reads a line into the character buffer. \r\n is converted to \n.
0721: *
0722: * @param buf character buffer to fill.
0723: * @param length number of characters to fill.
0724: *
0725: * @return -1 on end of file or the number of characters read.
0726: */
0727: public final int readLine(char[] buf, int length)
0728: throws IOException {
0729: return readLine(buf, length, true);
0730: }
0731:
0732: /**
0733: * Reads a line into the character buffer. \r\n is converted to \n.
0734: *
0735: * @param buf character buffer to fill.
0736: * @param length number of characters to fill.
0737: *
0738: * @return -1 on end of file or the number of characters read.
0739: */
0740: public final int readLine(char[] buf, int length, boolean isChop)
0741: throws IOException {
0742: byte[] readBuffer = _readBuffer;
0743:
0744: int offset = 0;
0745:
0746: while (true) {
0747: int readOffset = _readOffset;
0748:
0749: int sublen = _readLength - readOffset;
0750: if (sublen < length)
0751: sublen = length;
0752:
0753: for (; sublen > 0; sublen--) {
0754: int ch = readBuffer[readOffset++] & 0xff;
0755:
0756: if (ch != '\n') {
0757: } else if (isChop) {
0758: _readOffset = readOffset;
0759:
0760: if (offset > 0 && buf[offset - 1] == '\r')
0761: return offset - 1;
0762: else
0763: return offset;
0764: } else {
0765: buf[offset++] = (char) ch;
0766:
0767: _readOffset = readOffset;
0768:
0769: return offset + 1;
0770: }
0771:
0772: buf[offset++] = (char) ch;
0773: }
0774: _readOffset = readOffset;
0775:
0776: if (readOffset <= _readLength) {
0777: if (!readBuffer()) {
0778: return offset;
0779: }
0780: }
0781:
0782: if (length <= offset)
0783: return length + 1;
0784: }
0785: }
0786:
0787: private boolean readlnEncoded(CharBuffer cb, boolean isChop)
0788: throws IOException {
0789: while (true) {
0790: int ch = readChar();
0791:
0792: if (ch < 0)
0793: return cb.length() > 0;
0794:
0795: if (ch != '\n') {
0796: } else if (isChop) {
0797: if (cb.length() > 0 && cb.getLastChar() == '\r')
0798: cb.setLength(cb.getLength() - 1);
0799:
0800: return true;
0801: } else {
0802: cb.append('\n');
0803:
0804: return true;
0805: }
0806:
0807: cb.append((char) ch);
0808: }
0809: }
0810:
0811: /**
0812: * Copies this stream to the output stream.
0813: *
0814: * @param os destination stream.
0815: */
0816: public void writeToStream(OutputStream os) throws IOException {
0817: if (_readLength <= _readOffset) {
0818: readBuffer();
0819: }
0820:
0821: while (_readOffset < _readLength) {
0822: os.write(_readBuffer, _readOffset, _readLength
0823: - _readOffset);
0824:
0825: readBuffer();
0826: }
0827: }
0828:
0829: /**
0830: * Writes <code>len<code> bytes to the output stream from this stream.
0831: *
0832: * @param os destination stream.
0833: * @param len bytes to write.
0834: */
0835: public void writeToStream(OutputStream os, int len)
0836: throws IOException {
0837: while (len > 0) {
0838: if (_readLength <= _readOffset) {
0839: if (!readBuffer())
0840: return;
0841: }
0842:
0843: int sublen = _readLength - _readOffset;
0844: if (len < sublen)
0845: sublen = len;
0846:
0847: os.write(_readBuffer, _readOffset, sublen);
0848: _readOffset += sublen;
0849: len -= sublen;
0850: }
0851: }
0852:
0853: /**
0854: * Copies this stream to the output stream.
0855: *
0856: * @param out destination writer
0857: */
0858: public void writeToWriter(Writer out) throws IOException {
0859: int ch;
0860: while ((ch = readChar()) >= 0)
0861: out.write((char) ch);
0862: }
0863:
0864: /**
0865: * Fills the buffer from the underlying stream.
0866: */
0867: public int fillBuffer() throws IOException {
0868: if (!readBuffer())
0869: return -1;
0870: else
0871: return _readLength;
0872: }
0873:
0874: /**
0875: * Fills the buffer with a non-blocking read.
0876: */
0877: public boolean readNonBlock() throws IOException {
0878: if (_readOffset < _readLength)
0879: return true;
0880:
0881: if (_readBuffer == null) {
0882: _readOffset = 0;
0883: _readLength = 0;
0884: return false;
0885: }
0886:
0887: if (_sibling != null)
0888: _sibling.flush();
0889:
0890: _readOffset = 0;
0891: _readLength = _source.readNonBlock(_readBuffer, 0,
0892: _readBuffer.length);
0893:
0894: // Setting to 0 is needed to avoid int to long conversion errors with AIX
0895: if (_readLength > 0) {
0896: _position += _readLength;
0897: _readTime = Alarm.getCurrentTime();
0898:
0899: return true;
0900: } else {
0901: _readLength = 0;
0902: return false;
0903: }
0904: }
0905:
0906: /**
0907: * Fills the buffer with a non-blocking read.
0908: */
0909: public boolean fillWithTimeout(long timeout) throws IOException {
0910: if (_readOffset < _readLength)
0911: return true;
0912:
0913: if (_readBuffer == null) {
0914: _readOffset = 0;
0915: _readLength = 0;
0916: return false;
0917: }
0918:
0919: if (_sibling != null)
0920: _sibling.flush();
0921:
0922: _readOffset = 0;
0923: _readLength = _source.readTimeout(_readBuffer, 0,
0924: _readBuffer.length, timeout);
0925:
0926: // Setting to 0 is needed to avoid int to long conversion errors with AIX
0927: if (_readLength > 0) {
0928: _position += _readLength;
0929: _readTime = Alarm.getCurrentTime();
0930: return true;
0931: } else {
0932: _readLength = 0;
0933: return false;
0934: }
0935: }
0936:
0937: /**
0938: * Fills the read buffer, flushing the write buffer.
0939: *
0940: * @return false on end of file and true if there's more data.
0941: */
0942: private boolean readBuffer() throws IOException {
0943: if (_readBuffer == null) {
0944: _readOffset = 0;
0945: _readLength = 0;
0946: return false;
0947: }
0948:
0949: if (_sibling != null)
0950: _sibling.flush();
0951:
0952: _readOffset = 0;
0953: _readLength = _source.read(_readBuffer, 0, _readBuffer.length);
0954:
0955: // Setting to 0 is needed to avoid int to long conversion errors with AIX
0956: if (_readLength > 0) {
0957: _position += _readLength;
0958: _readTime = Alarm.getCurrentTime();
0959: return true;
0960: } else {
0961: _readLength = 0;
0962: return false;
0963: }
0964: }
0965:
0966: private boolean readBuffer(int off) throws IOException {
0967: if (_readBuffer == null)
0968: return false;
0969:
0970: if (_sibling != null)
0971: _sibling.flush();
0972:
0973: _readOffset = 0;
0974: _readLength = _source.read(_readBuffer, off, _readBuffer.length
0975: - off);
0976:
0977: // Setting to 0 is needed to avoid int to long conversion errors with AIX
0978: if (_readLength > 0) {
0979: _position += _readLength;
0980: _readTime = Alarm.getCurrentTime();
0981: return true;
0982: } else {
0983: _readLength = 0;
0984: return false;
0985: }
0986: }
0987:
0988: /**
0989: * Disables close. Sometimes an application will pass a stream
0990: * to a client that may close the stream at an inappropriate time.
0991: * Setting disable close gives the calling routine control over closing
0992: * the stream.
0993: */
0994: public void setDisableClose(boolean disableClose) {
0995: _disableClose = disableClose;
0996: }
0997:
0998: /**
0999: * Disables closing of the underlying source.
1000: */
1001: public void setDisableCloseSource(boolean disableClose) {
1002: _isDisableCloseSource = disableClose;
1003: }
1004:
1005: /**
1006: * Close the stream.
1007: */
1008: @Override
1009: public final void close() {
1010: try {
1011: if (_disableClose)
1012: return;
1013:
1014: if (!_reuseBuffer) {
1015: if (_tempRead != null) {
1016: TempBuffer.free(_tempRead);
1017: _tempRead = null;
1018: }
1019: _readBuffer = null;
1020: }
1021:
1022: if (_readEncoding != null) {
1023: Reader reader = _readEncoding;
1024: _readEncoding = null;
1025: reader.close();
1026: }
1027:
1028: if (_source != null && !_isDisableCloseSource) {
1029: StreamImpl s = _source;
1030: _source = null;
1031: s.close();
1032: }
1033: } catch (IOException e) {
1034: log().log(Level.FINE, e.toString(), e);
1035: }
1036: }
1037:
1038: /**
1039: * Returns a named attribute. For example, an HTTP stream
1040: * may use this to return header values.
1041: */
1042: public Object getAttribute(String name) throws IOException {
1043: if (_sibling != null)
1044: _sibling.flush();
1045:
1046: return _source.getAttribute(name);
1047: }
1048:
1049: /**
1050: * Lists all named attributes.
1051: */
1052: public Iterator getAttributeNames() throws IOException {
1053: if (_sibling != null)
1054: _sibling.flush();
1055:
1056: return _source.getAttributeNames();
1057: }
1058:
1059: /**
1060: * Sets a named attribute. For example, an HTTP stream
1061: * may use this to set header values.
1062: */
1063: public void setAttribute(String name, Object value)
1064: throws IOException {
1065: _source.setAttribute(name, value);
1066: }
1067:
1068: /**
1069: * Removes a named attribute.
1070: */
1071: public void removeAttribute(String name) throws IOException {
1072: _source.removeAttribute(name);
1073: }
1074:
1075: /**
1076: * Returns the Path which opened this stream.
1077: */
1078: public Path getPath() {
1079: return _source == null ? null : _source.getPath();
1080: }
1081:
1082: /**
1083: * Returns the user path which opened this stream.
1084: *
1085: * <p>Parsing routines typically use this for error reporting.
1086: */
1087: public String getUserPath() {
1088: if (_source == null || _source.getPath() == null)
1089: return "stream";
1090: else
1091: return _source.getPath().getUserPath();
1092: }
1093:
1094: /**
1095: * Returns the user path which opened this stream.
1096: *
1097: * <p>Parsing routines typically use this for error reporting.
1098: */
1099: public String getURL() {
1100: if (_source == null || _source.getPath() == null)
1101: return "stream:";
1102: else
1103: return _source.getPath().getURL();
1104: }
1105:
1106: /**
1107: * Sets a path name associated with the stream.
1108: */
1109: public void setPath(Path path) {
1110: _source.setPath(path);
1111: }
1112:
1113: /**
1114: * Returns a Reader reading to this stream.
1115: */
1116: public Reader getReader() {
1117: if (_reader == null)
1118: _reader = new StreamReader();
1119:
1120: return _reader;
1121: }
1122:
1123: private static Logger log() {
1124: return Logger.getLogger(ReadStream.class.getName());
1125: }
1126:
1127: /**
1128: * Returns a printable representation of the read stream.
1129: */
1130: public String toString() {
1131: return "ReadStream[" + _source + "]";
1132: }
1133:
1134: public boolean lock(boolean shared, boolean block) {
1135: if (!(_source instanceof LockableStream))
1136: return true;
1137:
1138: LockableStream ls = (LockableStream) _source;
1139: return ls.lock(shared, block);
1140: }
1141:
1142: public boolean unlock() {
1143: if (!(_source instanceof LockableStream))
1144: return true;
1145:
1146: LockableStream ls = (LockableStream) _source;
1147: return ls.unlock();
1148: }
1149:
1150: public class StreamReader extends Reader {
1151: public final int read() throws IOException {
1152: return ReadStream.this .readChar();
1153: }
1154:
1155: public final int read(char[] cbuf, int off, int len)
1156: throws IOException {
1157: return ReadStream.this .read(cbuf, off, len);
1158: }
1159:
1160: public boolean ready() throws IOException {
1161: return ReadStream.this .available() > 0;
1162: }
1163:
1164: public final void close() throws IOException {
1165: }
1166:
1167: public ReadStream getStream() {
1168: return ReadStream.this;
1169: }
1170: }
1171: }
|