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.env.Env;
033: import com.caucho.quercus.env.EnvCleanup;
034: import com.caucho.quercus.env.StringValue;
035: import com.caucho.quercus.env.Value;
036: import com.caucho.vfs.Encoding;
037: import com.caucho.vfs.FilePath;
038: import com.caucho.vfs.Path;
039: import com.caucho.vfs.RandomAccessStream;
040: import com.caucho.vfs.TempBuffer;
041: import com.caucho.vfs.LockableStream;
042:
043: import java.io.*;
044: import java.util.logging.Level;
045: import java.util.logging.Logger;
046:
047: /**
048: * Represents a PHP open file
049: */
050: public class FileInputOutput extends AbstractBinaryOutput implements
051: BinaryInput, BinaryOutput, LockableStream, EnvCleanup {
052: private static final Logger log = Logger
053: .getLogger(FileInputOutput.class.getName());
054:
055: private Env _env;
056: private Path _path;
057: private LineReader _lineReader;
058:
059: private RandomAccessStream _stream;
060: private int _buffer;
061: private boolean _doUnread = false;
062:
063: private Reader _readEncoding;
064: private String _readEncodingName;
065:
066: private boolean _temporary;
067:
068: public FileInputOutput(Env env, Path path) throws IOException {
069: this (env, path, false, false, false);
070: }
071:
072: public FileInputOutput(Env env, Path path, boolean append,
073: boolean truncate) throws IOException {
074: this (env, path, append, truncate, false);
075: }
076:
077: public FileInputOutput(Env env, Path path, boolean append,
078: boolean truncate, boolean temporary) throws IOException {
079: _env = env;
080:
081: env.addCleanup(this );
082:
083: _path = path;
084:
085: _lineReader = new LineReader(env);
086:
087: if (truncate)
088: path.truncate(0L);
089:
090: _stream = path.openRandomAccess();
091:
092: if (append && _stream.getLength() > 0)
093: _stream.seek(_stream.getLength());
094:
095: _temporary = temporary;
096: }
097:
098: /**
099: * Returns the write stream.
100: */
101: public OutputStream getOutputStream() {
102: try {
103: return _stream.getOutputStream();
104: } catch (IOException e) {
105: log.log(Level.FINE, e.toString(), e);
106:
107: return null;
108: }
109: }
110:
111: /**
112: * Returns the read stream.
113: */
114: public InputStream getInputStream() {
115: try {
116: return _stream.getInputStream();
117: } catch (IOException e) {
118: log.log(Level.FINE, e.toString(), e);
119:
120: return null;
121: }
122: }
123:
124: /**
125: * Returns the path.
126: */
127: public Path getPath() {
128: return _path;
129: }
130:
131: /**
132: * Sets the current read encoding. The encoding can either be a
133: * Java encoding name or a mime encoding.
134: *
135: * @param encoding name of the read encoding
136: */
137: public void setEncoding(String encoding)
138: throws UnsupportedEncodingException {
139: String mimeName = Encoding.getMimeName(encoding);
140:
141: if (mimeName != null && mimeName.equals(_readEncodingName))
142: return;
143:
144: _readEncoding = Encoding.getReadEncoding(getInputStream(),
145: encoding);
146: _readEncodingName = mimeName;
147: }
148:
149: private int readChar() throws IOException {
150: if (_readEncoding != null) {
151: int ch = _readEncoding.read();
152: return ch;
153: }
154:
155: return read() & 0xff;
156: }
157:
158: /**
159: * Unread a character.
160: */
161: public void unread() throws IOException {
162: _doUnread = true;
163: }
164:
165: /**
166: * Reads a character from a file, returning -1 on EOF.
167: */
168: public int read() throws IOException {
169: if (_doUnread) {
170: _doUnread = false;
171:
172: return _buffer;
173: } else {
174: _buffer = _stream.read();
175:
176: return _buffer;
177: }
178: }
179:
180: /**
181: * Reads a buffer from a file, returning -1 on EOF.
182: */
183: public int read(byte[] buffer, int offset, int length)
184: throws IOException {
185: _doUnread = false;
186:
187: return _stream.read(buffer, offset, length);
188: }
189:
190: /**
191: * Reads a buffer from a file, returning -1 on EOF.
192: */
193: public int read(char[] buffer, int offset, int length)
194: throws IOException {
195: _doUnread = false;
196:
197: return _stream.read(buffer, offset, length);
198: }
199:
200: /**
201: * Appends to a string builder.
202: */
203: public StringValue appendTo(StringValue builder) throws IOException {
204: if (_stream != null)
205: return builder.append(_stream);
206: else
207: return builder;
208: }
209:
210: /**
211: * Reads a Binary string.
212: */
213: public StringValue read(int length) throws IOException {
214: StringValue bb = _env.createBinaryBuilder();
215: TempBuffer temp = TempBuffer.allocate();
216: byte[] buffer = temp.getBuffer();
217:
218: while (length > 0) {
219: int sublen = buffer.length;
220:
221: if (length < sublen)
222: sublen = length;
223:
224: sublen = read(buffer, 0, sublen);
225:
226: if (sublen > 0) {
227: bb.append(buffer, 0, sublen);
228: length -= sublen;
229: } else
230: break;
231: }
232:
233: TempBuffer.free(temp);
234:
235: return bb;
236: }
237:
238: /**
239: * Reads the optional linefeed character from a \r\n
240: */
241: public boolean readOptionalLinefeed() throws IOException {
242: int ch = read();
243:
244: if (ch == '\n') {
245: return true;
246: } else {
247: unread();
248: return false;
249: }
250: }
251:
252: /**
253: * Reads a line from the buffer.
254: */
255: public StringValue readLine(long length) throws IOException {
256: return _lineReader.readLine(_env, this , length);
257: }
258:
259: /**
260: * Returns true on the EOF.
261: */
262: public boolean isEOF() {
263: try {
264: return _stream.getLength() <= _stream.getFilePointer();
265: } catch (IOException e) {
266: return true;
267: }
268: }
269:
270: /**
271: * Prints a string to a file.
272: */
273: public void print(char v) throws IOException {
274: _stream.write((byte) v);
275: }
276:
277: /**
278: * Prints a string to a file.
279: */
280: public void print(String v) throws IOException {
281: for (int i = 0; i < v.length(); i++)
282: write(v.charAt(i));
283: }
284:
285: /**
286: * Writes a buffer to a file.
287: */
288: public void write(byte[] buffer, int offset, int length)
289: throws IOException {
290: _stream.write(buffer, offset, length);
291: }
292:
293: /**
294: * Writes a buffer to a file.
295: */
296: public void write(int ch) throws IOException {
297: _stream.write(ch);
298: }
299:
300: /**
301: * Flushes the output.
302: */
303: public void flush() throws IOException {
304: }
305:
306: /**
307: * Closes the file for writing.
308: */
309: public void closeWrite() {
310: close();
311: }
312:
313: /**
314: * Closes the file for reading.
315: */
316: public void closeRead() {
317: close();
318: }
319:
320: /**
321: * Closes the file.
322: */
323: public void close() {
324: _env.removeCleanup(this );
325:
326: cleanup();
327: }
328:
329: /**
330: * Implements the EnvCleanup interface.
331: */
332: public void cleanup() {
333: try {
334: RandomAccessStream ras = _stream;
335: _stream = null;
336:
337: if (ras != null) {
338: ras.close();
339:
340: if (_temporary)
341: _path.remove();
342: }
343: } catch (IOException e) {
344: log.log(Level.FINE, e.toString(), e);
345: }
346: }
347:
348: /**
349: * Returns the current location in the file.
350: */
351: public long getPosition() {
352: try {
353: return _stream.getFilePointer();
354: } catch (IOException e) {
355: log.log(Level.FINE, e.toString(), e);
356:
357: return -1;
358: }
359: }
360:
361: /**
362: * Sets the current location in the stream
363: */
364: public boolean setPosition(long offset) {
365: return _stream.seek(offset);
366: }
367:
368: public long seek(long offset, int whence) {
369: switch (whence) {
370: case BinaryInput.SEEK_CUR:
371: offset = getPosition() + offset;
372: break;
373: case BinaryInput.SEEK_END:
374: try {
375: offset = _stream.getLength() + offset;
376: } catch (IOException e) {
377: log.log(Level.FINE, e.toString(), e);
378:
379: return getPosition();
380: }
381: break;
382: case SEEK_SET:
383: default:
384: break;
385: }
386:
387: _stream.seek(offset);
388:
389: return offset;
390: }
391:
392: /**
393: * Opens a copy.
394: */
395: public BinaryInput openCopy() throws IOException {
396: return new FileInputOutput(_env, _path);
397: }
398:
399: /**
400: * Lock the shared advisory lock.
401: */
402: public boolean lock(boolean shared, boolean block) {
403: return _stream.lock(shared, block);
404: }
405:
406: /**
407: * Unlock the advisory lock.
408: */
409: public boolean unlock() {
410: return _stream.unlock();
411: }
412:
413: public Value stat() {
414: return FileModule.statImpl(_env, getPath());
415: }
416:
417: /**
418: * Converts to a string.
419: * @param env
420: */
421: public String toString() {
422: return "FileInputOutput[" + getPath() + "]";
423: }
424: }
|