001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Emil Ong
028: */
029:
030: package com.caucho.quercus.lib.file;
031:
032: import com.caucho.quercus.QuercusModuleException;
033: import com.caucho.quercus.env.*;
034: import com.caucho.vfs.TempBuffer;
035:
036: import java.io.IOException;
037: import java.io.InputStream;
038: import java.io.OutputStream;
039: import java.io.UnsupportedEncodingException;
040:
041: /**
042: * A stream that has its operations mediated by a Quercus object.
043: */
044: public class WrappedStream implements BinaryInput, BinaryOutput {
045: private static final UnicodeBuilderValue STREAM_CLOSE = new UnicodeBuilderValue(
046: "stream_close");
047: private static final UnicodeBuilderValue STREAM_EOF = new UnicodeBuilderValue(
048: "stream_eof");
049: private static final UnicodeBuilderValue STREAM_FLUSH = new UnicodeBuilderValue(
050: "stream_flush");
051: private static final UnicodeBuilderValue STREAM_OPEN = new UnicodeBuilderValue(
052: "stream_open");
053: private static final UnicodeBuilderValue STREAM_READ = new UnicodeBuilderValue(
054: "stream_read");
055: private static final UnicodeBuilderValue STREAM_SEEK = new UnicodeBuilderValue(
056: "stream_seek");
057: private static final UnicodeBuilderValue STREAM_TELL = new UnicodeBuilderValue(
058: "stream_tell");
059: private static final UnicodeBuilderValue STREAM_WRITE = new UnicodeBuilderValue(
060: "stream_write");
061:
062: private byte[] printBuffer = new byte[1];
063:
064: private Env _env;
065: private Value _wrapper;
066: private LineReader _lineReader;
067:
068: private InputStream _is;
069: private OutputStream _os;
070: private int _buffer;
071: private boolean _doUnread = false;
072:
073: private int _writeLength;
074:
075: private WrappedStream(Env env, Value wrapper) {
076: _env = env;
077:
078: _wrapper = wrapper;
079:
080: _lineReader = new LineReader(env);
081: }
082:
083: public WrappedStream(Env env, QuercusClass qClass,
084: StringValue path, StringValue mode, LongValue options) {
085: _env = env;
086:
087: _lineReader = new LineReader(env);
088:
089: _wrapper = qClass.callNew(_env, new Value[0]);
090:
091: _wrapper.callMethod(_env, STREAM_OPEN, path, mode, options,
092: NullValue.NULL);
093: }
094:
095: public InputStream getInputStream() {
096: if (_is == null)
097: _is = new WrappedInputStream();
098:
099: return _is;
100: }
101:
102: public OutputStream getOutputStream() {
103: if (_os == null)
104: _os = new WrappedOutputStream();
105:
106: return _os;
107: }
108:
109: /**
110: * Opens a new copy.
111: */
112: public BinaryInput openCopy() throws IOException {
113: return new WrappedStream(_env, _wrapper);
114: }
115:
116: /**
117: * Sets the current read encoding. The encoding can either be a
118: * Java encoding name or a mime encoding.
119: *
120: * @param encoding name of the read encoding
121: */
122: public void setEncoding(String encoding)
123: throws UnsupportedEncodingException {
124: }
125:
126: public void closeRead() {
127: close();
128: }
129:
130: public void closeWrite() {
131: close();
132: }
133:
134: public void close() {
135: _wrapper.callMethod(_env, STREAM_CLOSE);
136: }
137:
138: /**
139: * Reads a character from a file, returning -1 on EOF.
140: */
141: public int read() throws IOException {
142: if (_doUnread) {
143: _doUnread = false;
144:
145: return _buffer;
146: } else {
147: Value output = _wrapper.callMethod(_env, STREAM_READ,
148: LongValue.ONE);
149:
150: _buffer = (int) output.toLong();
151:
152: return _buffer;
153: }
154: }
155:
156: /**
157: * Unread a character.
158: */
159: public void unread() throws IOException {
160: _doUnread = true;
161: }
162:
163: public int read(byte[] buffer, int offset, int length) {
164: // XXX: shgould be reimplemented
165:
166: Value output = _wrapper.callMethod(_env, STREAM_READ, LongValue
167: .create(length));
168:
169: // XXX "0"?
170:
171: if (!output.toBoolean())
172: return -1;
173:
174: byte[] outputBytes = output.toString().getBytes();
175:
176: if (outputBytes.length < length)
177: length = outputBytes.length;
178:
179: System.arraycopy(outputBytes, 0, buffer, offset, length);
180:
181: return length;
182: }
183:
184: public int read(char[] buffer, int offset, int length) {
185: // XXX: shgould be reimplemented
186:
187: Value output = _wrapper.callMethod(_env, STREAM_READ, LongValue
188: .create(length));
189:
190: // XXX "0"?
191:
192: if (!output.toBoolean())
193: return -1;
194:
195: byte[] outputBytes = output.toString().getBytes();
196:
197: if (outputBytes.length < length)
198: length = outputBytes.length;
199:
200: System.arraycopy(outputBytes, 0, buffer, offset, length);
201:
202: return length;
203: }
204:
205: /**
206: * Appends to a string builder.
207: */
208: public StringValue appendTo(StringValue builder) {
209: try {
210: int ch;
211:
212: while ((ch = read()) >= 0) {
213: builder.append((char) ch);
214: }
215:
216: return builder;
217: } catch (IOException e) {
218: throw new QuercusModuleException(e);
219: }
220: }
221:
222: /**
223: * Reads a Binary string.
224: */
225: public StringValue read(int length) throws IOException {
226: Value output = _wrapper.callMethod(_env, STREAM_READ, LongValue
227: .create(length));
228:
229: return output.toBinaryValue(_env);
230: }
231:
232: /**
233: * Reads the optional linefeed character from a \r\n
234: */
235: public boolean readOptionalLinefeed() throws IOException {
236: int ch = read();
237:
238: if (ch == '\n') {
239: return true;
240: } else {
241: unread();
242: return false;
243: }
244: }
245:
246: /**
247: * Reads a line from a file, returning null on EOF.
248: */
249: public StringValue readLine(long length) throws IOException {
250: return _lineReader.readLine(_env, this , length);
251: }
252:
253: public void write(byte[] buffer, int offset, int length)
254: throws IOException {
255: StringValue bb = _env.createBinaryBuilder(buffer, offset,
256: length);
257:
258: Value output = _wrapper.callMethod(_env, STREAM_WRITE, bb);
259:
260: _writeLength = (int) output.toLong();
261: }
262:
263: /**
264: * Writes to a stream.
265: */
266: public int write(InputStream is, int length) {
267: int writeLength = 0;
268:
269: TempBuffer tb = TempBuffer.allocate();
270: byte[] buffer = tb.getBuffer();
271:
272: try {
273: while (length > 0) {
274: int sublen;
275:
276: if (length < buffer.length)
277: sublen = length;
278: else
279: sublen = buffer.length;
280:
281: sublen = is.read(buffer, 0, sublen);
282:
283: if (sublen < 0)
284: break;
285:
286: for (int offset = 0; offset < sublen;) {
287: write(buffer, offset, sublen);
288:
289: if (_writeLength > 0)
290: offset += _writeLength;
291: else
292: return writeLength;
293: }
294:
295: writeLength += sublen;
296: length -= sublen;
297: }
298:
299: return writeLength;
300: } catch (IOException e) {
301: throw new QuercusModuleException(e);
302: } finally {
303: TempBuffer.free(tb);
304: }
305: }
306:
307: /**
308: * Prints a string to a file.
309: */
310: public void print(char v) throws IOException {
311: printBuffer[0] = (byte) v;
312:
313: write(printBuffer, 0, 1);
314: }
315:
316: /**
317: * Prints a string to a file.
318: */
319: public void print(String v) throws IOException {
320: for (int i = 0; i < v.length(); i++)
321: print(v.charAt(i));
322: }
323:
324: /**
325: * Returns true if end-of-file has been reached
326: */
327: public boolean isEOF() {
328: return _wrapper.callMethod(_env, STREAM_EOF).toBoolean();
329: }
330:
331: /**
332: * Tells the position in the stream
333: */
334: public long getPosition() {
335: return _wrapper.callMethod(_env, STREAM_TELL).toLong();
336: }
337:
338: /**
339: * Sets the position.
340: */
341: public boolean setPosition(long offset) {
342: LongValue offsetValue = LongValue.create(offset);
343: LongValue whenceValue = LongValue.create(SEEK_SET);
344:
345: return _wrapper.callMethod(_env, STREAM_SEEK, offsetValue,
346: whenceValue).toBoolean();
347: }
348:
349: public long seek(long offset, int whence) {
350: LongValue offsetValue = LongValue.create(offset);
351: LongValue whenceValue = LongValue.create(whence);
352:
353: return _wrapper.callMethod(_env, STREAM_SEEK, offsetValue,
354: whenceValue).toLong();
355: }
356:
357: public void flush() throws IOException {
358: if (!_wrapper.callMethod(_env, STREAM_FLUSH).toBoolean())
359: throw new IOException(); // Get around java.io.Flushable
360: }
361:
362: public Value stat() {
363: return _wrapper.callMethod(_env, STREAM_FLUSH);
364: }
365:
366: private class WrappedInputStream extends InputStream {
367: public int read() throws IOException {
368: return WrappedStream.this .read();
369: }
370: }
371:
372: private class WrappedOutputStream extends OutputStream {
373: public void write(int b) throws IOException {
374: _wrapper
375: .callMethod(_env, STREAM_WRITE, LongValue.create(b));
376: }
377: }
378: }
|