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 Scott Ferguson
028: */
029:
030: package com.caucho.server.connection;
031:
032: import com.caucho.log.Log;
033: import com.caucho.util.L10N;
034: import com.caucho.vfs.Encoding;
035: import com.caucho.vfs.TempCharBuffer;
036:
037: import java.io.IOException;
038: import java.io.InputStream;
039: import java.io.Reader;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: abstract public class ToCharResponseStream extends
044: AbstractResponseStream {
045: static final Logger log = Log.open(ToCharResponseStream.class);
046:
047: static final L10N L = new L10N(ToCharResponseStream.class);
048:
049: private static final int SIZE = TempCharBuffer.SIZE;
050:
051: private final byte[] _byteBuffer = new byte[SIZE];
052:
053: // head of the expandable buffer
054: private TempCharBuffer _head = TempCharBuffer.allocate();
055: private TempCharBuffer _tail;
056:
057: private char[] _charBuffer;
058: private int _charLength;
059:
060: private int _bufferCapacity;
061: private int _bufferSize;
062:
063: private BufferInputStream _in;
064: private Reader _encodingReader;
065:
066: public ToCharResponseStream() {
067: }
068:
069: /**
070: * Initializes the Buffered Response stream at the beginning of a request.
071: */
072: public void start() {
073: super .start();
074:
075: _head.clear();
076: _head.setNext(null);
077: _tail = _head;
078:
079: _charBuffer = _tail.getBuffer();
080: _charLength = 0;
081:
082: _bufferCapacity = SIZE;
083: _bufferSize = 0;
084:
085: _encodingReader = null;
086: /*
087: _autoFlush = true;
088: _isClosed = false;
089: */
090:
091: // _toChar = Encoding.getReadEncoding("ISO-8859-1");
092: }
093:
094: /**
095: * Returns true for a caucho response stream.
096: */
097: public boolean isCauchoResponseStream() {
098: return true;
099: }
100:
101: /**
102: * Sets the buffer size.
103: */
104: public void setBufferSize(int size) {
105: size = (size + SIZE - 1);
106: size -= size % SIZE;
107:
108: if (_bufferCapacity < size)
109: _bufferCapacity = size;
110: }
111:
112: /**
113: * Gets the buffer size.
114: */
115: public int getBufferSize() {
116: return _bufferCapacity;
117: }
118:
119: /**
120: * Returns the remaining buffer entries.
121: */
122: public int getRemaining() {
123: return _bufferCapacity - _bufferSize - _charLength;
124: }
125:
126: /**
127: * Returns the char buffer.
128: */
129: public char[] getCharBuffer() {
130: return _charBuffer;
131: }
132:
133: /**
134: * Returns the char offset.
135: */
136: public int getCharOffset() {
137: return _charLength;
138: }
139:
140: /**
141: * Sets the char offset.
142: */
143: public void setCharOffset(int offset) throws IOException {
144: _charLength = offset;
145:
146: if (SIZE <= _charLength)
147: expandCharBuffer();
148: }
149:
150: /**
151: * Converts the char buffer.
152: */
153: public char[] nextCharBuffer(int offset) throws IOException {
154: _charLength = offset;
155:
156: if (SIZE <= _charLength)
157: expandCharBuffer();
158:
159: return _charBuffer;
160: }
161:
162: /**
163: * Writes a character to the output.
164: */
165: public void print(int ch) throws IOException {
166: /*
167: if (_isClosed)
168: return;
169: */
170: _charBuffer[_charLength++] = (char) ch;
171:
172: if (SIZE <= _charLength)
173: expandCharBuffer();
174: }
175:
176: /**
177: * Writes a char array to the output.
178: */
179: public void print(char[] buffer, int offset, int length)
180: throws IOException {
181: /*
182: if (_isHead || _isClosed)
183: return;
184:
185: if (_byteLength > 0)
186: flushByteBuffer();
187: */
188:
189: int charLength = _charLength;
190: while (length > 0) {
191: int sublen = length;
192: if (SIZE - charLength < sublen)
193: sublen = SIZE - charLength;
194:
195: System.arraycopy(buffer, offset, _charBuffer, charLength,
196: sublen);
197:
198: offset += sublen;
199: length -= sublen;
200: charLength += sublen;
201:
202: if (SIZE <= charLength) {
203: _charLength = charLength;
204: expandCharBuffer();
205: charLength = _charLength;
206: }
207: }
208:
209: _charLength = charLength;
210: }
211:
212: /**
213: * Returns the buffer offset.
214: */
215: public int getBufferOffset() {
216: return 0;
217: }
218:
219: /**
220: * Sets the byte buffer offset.
221: */
222: public void setBufferOffset(int offset) {
223: if (offset > 0) {
224: try {
225: write(_byteBuffer, 0, offset);
226: } catch (IOException e) {
227: log.log(Level.FINE, e.toString(), e);
228: }
229: }
230: }
231:
232: /**
233: * Gets the byte buffer
234: */
235: public byte[] getBuffer() {
236: return _byteBuffer;
237: }
238:
239: /**
240: * Returns the next buffer.
241: */
242: public byte[] nextBuffer(int offset) throws IOException {
243: if (offset > 0)
244: write(_byteBuffer, 0, offset);
245:
246: return _byteBuffer;
247: }
248:
249: /**
250: * Writes the next chunk of data to the response stream.
251: *
252: * @param buf the buffer containing the data
253: * @param offset start offset into the buffer
254: * @param length length of the data in the buffer
255: */
256: public void write(byte[] buf, int offset, int length)
257: throws IOException {
258: if (length == 0)
259: return;
260:
261: if (_encodingReader == null) {
262: // server/1b13
263: if (_in == null)
264: _in = new BufferInputStream();
265: _encodingReader = Encoding.getReadEncoding(_in,
266: getEncoding());
267: }
268:
269: // XXX: performance issues
270: if (_encodingReader == null) {
271: for (; length > 0; length--) {
272: print((char) buf[offset++]);
273: }
274: return;
275: }
276:
277: _in.init(buf, offset, length);
278:
279: // XXX: performance issues
280: int ch;
281: while ((ch = _encodingReader.read()) >= 0) {
282: print(ch);
283: }
284: }
285:
286: /**
287: * Writes the next chunk of data to the response stream.
288: *
289: * @param buf the buffer containing the data
290: * @param offset start offset into the buffer
291: * @param length length of the data in the buffer
292: */
293: public void write(int ch) throws IOException {
294: if (_encodingReader == null) {
295: if (_in == null)
296: _in = new BufferInputStream();
297: _byteBuffer[0] = (byte) ch;
298: _encodingReader = Encoding.getReadEncoding(_in,
299: getEncoding());
300:
301: if (_encodingReader == null) {
302: print((char) ch);
303: return;
304: }
305: }
306:
307: _byteBuffer[0] = (byte) ch;
308: _in.init(_byteBuffer, 0, 1);
309:
310: while ((ch = _encodingReader.read()) >= 0) {
311: print(ch);
312: }
313: }
314:
315: /**
316: * Flushes the buffer.
317: */
318: public void flushBuffer() throws IOException {
319: flushCharBuffer();
320: }
321:
322: /**
323: * Flushes the buffer.
324: */
325: public void flushChar() throws IOException {
326: flushCharBuffer();
327: }
328:
329: /**
330: * Flushes or writes to the buffer.
331: */
332: private void expandCharBuffer() throws IOException {
333: if (_bufferCapacity <= _bufferSize + _charLength) {
334: flushCharBuffer();
335: } else if (_charLength == SIZE) {
336: _tail.setLength(_charLength);
337: _bufferSize += _charLength;
338:
339: TempCharBuffer tempBuf = TempCharBuffer.allocate();
340: _tail.setNext(tempBuf);
341: _tail = tempBuf;
342:
343: _charBuffer = _tail.getBuffer();
344: _charLength = 0;
345: }
346: }
347:
348: /**
349: * Flushes the buffered response to the output stream.
350: */
351: private void flushCharBuffer() throws IOException {
352: _tail.setLength(_charLength);
353: _bufferSize += _charLength;
354: _charLength = 0;
355:
356: TempCharBuffer ptr = _head;
357: do {
358: _head = ptr;
359:
360: TempCharBuffer next = ptr.getNext();
361: ptr.setNext(null);
362:
363: writeNext(ptr.getBuffer(), 0, ptr.getLength());
364:
365: if (next != null)
366: TempCharBuffer.free(ptr);
367:
368: ptr = next;
369: } while (ptr != null);
370:
371: _tail = _head;
372: _tail.setLength(0);
373: _charBuffer = _tail.getBuffer();
374: _bufferSize = 0;
375: }
376:
377: /**
378: * Flushes the buffer.
379: */
380: public void clearBuffer() {
381: _charLength = 0;
382:
383: TempCharBuffer ptr = _head;
384: do {
385: _head = ptr;
386:
387: TempCharBuffer next = ptr.getNext();
388: ptr.setNext(null);
389:
390: if (next != null)
391: TempCharBuffer.free(ptr);
392:
393: ptr = next;
394: } while (ptr != null);
395:
396: _tail = _head;
397: _tail.setLength(0);
398: _charBuffer = _head.getBuffer();
399: _bufferSize = 0;
400: }
401:
402: /**
403: * Returns the encoding.
404: */
405: abstract protected String getEncoding();
406:
407: /**
408: * Writes to the next.
409: */
410: abstract protected void writeNext(char[] buffer, int offset,
411: int length) throws IOException;
412:
413: static class BufferInputStream extends InputStream {
414: private byte[] _buffer;
415: private int _offset;
416: private int _length;
417:
418: void init(byte[] buffer, int offset, int length) {
419: _buffer = buffer;
420: _offset = offset;
421: _length = length;
422: }
423:
424: public int read() {
425: if (_offset < _length)
426: return _buffer[_offset++] & 0xff;
427: else
428: return -1;
429: }
430: }
431: }
|