001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.tomcat.util.buf;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.InputStreamReader;
022: import java.io.UnsupportedEncodingException;
023:
024: /** Efficient conversion of bytes to character .
025: *
026: * This uses the standard JDK mechansim - a reader - but provides mechanisms
027: * to recycle all the objects that are used. It is compatible with JDK1.1
028: * and up,
029: * ( nio is better, but it's not available even in 1.2 or 1.3 )
030: *
031: * Not used in the current code, the performance gain is not very big
032: * in the current case ( since String is created anyway ), but it will
033: * be used in a later version or after the remaining optimizations.
034: */
035: public class B2CConverter {
036: private IntermediateInputStream iis;
037: private ReadConvertor conv;
038: private String encoding;
039:
040: protected B2CConverter() {
041: }
042:
043: /** Create a converter, with bytes going to a byte buffer
044: */
045: public B2CConverter(String encoding) throws IOException {
046: this .encoding = encoding;
047: reset();
048: }
049:
050: /** Reset the internal state, empty the buffers.
051: * The encoding remain in effect, the internal buffers remain allocated.
052: */
053: public void recycle() {
054: conv.recycle();
055: }
056:
057: static final int BUFFER_SIZE = 8192;
058: char result[] = new char[BUFFER_SIZE];
059:
060: /** Convert a buffer of bytes into a chars
061: */
062: public void convert(ByteChunk bb, CharChunk cb) throws IOException {
063: // Set the ByteChunk as input to the Intermediate reader
064: iis.setByteChunk(bb);
065: convert(cb);
066: }
067:
068: private void convert(CharChunk cb) throws IOException {
069: try {
070: // read from the reader
071: while (true) { // conv.ready() ) {
072: int cnt = conv.read(result, 0, BUFFER_SIZE);
073: if (cnt <= 0) {
074: // End of stream ! - we may be in a bad state
075: if (debug > 0)
076: log("EOF");
077: // reset();
078: return;
079: }
080: if (debug > 1)
081: log("Converted: " + new String(result, 0, cnt));
082:
083: // XXX go directly
084: cb.append(result, 0, cnt);
085: }
086: } catch (IOException ex) {
087: if (debug > 0)
088: log("Reseting the converter " + ex.toString());
089: reset();
090: throw ex;
091: }
092: }
093:
094: public void reset() throws IOException {
095: // destroy the reader/iis
096: iis = new IntermediateInputStream();
097: conv = new ReadConvertor(iis, encoding);
098: }
099:
100: private final int debug = 0;
101:
102: void log(String s) {
103: System.out.println("B2CConverter: " + s);
104: }
105:
106: // -------------------- Not used - the speed improvemnt is quite small
107:
108: /*
109: private Hashtable decoders;
110: public static final boolean useNewString=false;
111: public static final boolean useSpecialDecoders=true;
112: private UTF8Decoder utfD;
113: // private char[] conversionBuff;
114: CharChunk conversionBuf;
115:
116:
117: private static String decodeString(ByteChunk mb, String enc)
118: throws IOException
119: {
120: byte buff=mb.getBuffer();
121: int start=mb.getStart();
122: int end=mb.getEnd();
123: if( useNewString ) {
124: if( enc==null) enc="UTF8";
125: return new String( buff, start, end-start, enc );
126: }
127: B2CConverter b2c=null;
128: if( useSpecialDecoders &&
129: (enc==null || "UTF8".equalsIgnoreCase(enc))) {
130: if( utfD==null ) utfD=new UTF8Decoder();
131: b2c=utfD;
132: }
133: if(decoders == null ) decoders=new Hashtable();
134: if( enc==null ) enc="UTF8";
135: b2c=(B2CConverter)decoders.get( enc );
136: if( b2c==null ) {
137: if( useSpecialDecoders ) {
138: if( "UTF8".equalsIgnoreCase( enc ) ) {
139: b2c=new UTF8Decoder();
140: }
141: }
142: if( b2c==null )
143: b2c=new B2CConverter( enc );
144: decoders.put( enc, b2c );
145: }
146: if( conversionBuf==null ) conversionBuf=new CharChunk(1024);
147:
148: try {
149: conversionBuf.recycle();
150: b2c.convert( this, conversionBuf );
151: //System.out.println("XXX 1 " + conversionBuf );
152: return conversionBuf.toString();
153: } catch( IOException ex ) {
154: ex.printStackTrace();
155: return null;
156: }
157: }
158:
159: */
160: }
161:
162: // -------------------- Private implementation --------------------
163:
164: /**
165: *
166: */
167: final class ReadConvertor extends InputStreamReader {
168: // stream with flush() and close(). overriden.
169: private IntermediateInputStream iis;
170:
171: // Has a private, internal byte[8192]
172:
173: /** Create a converter.
174: */
175: public ReadConvertor(IntermediateInputStream in, String enc)
176: throws UnsupportedEncodingException {
177: super (in, enc);
178: iis = in;
179: }
180:
181: /** Overriden - will do nothing but reset internal state.
182: */
183: public final void close() throws IOException {
184: // NOTHING
185: // Calling super.close() would reset out and cb.
186: }
187:
188: public final int read(char cbuf[], int off, int len)
189: throws IOException {
190: // will do the conversion and call write on the output stream
191: return super .read(cbuf, off, len);
192: }
193:
194: /** Reset the buffer
195: */
196: public final void recycle() {
197: }
198: }
199:
200: /** Special output stream where close() is overriden, so super.close()
201: is never called.
202:
203: This allows recycling. It can also be disabled, so callbacks will
204: not be called if recycling the converter and if data was not flushed.
205: */
206: final class IntermediateInputStream extends InputStream {
207: byte buf[];
208: int pos;
209: int len;
210: int end;
211:
212: public IntermediateInputStream() {
213: }
214:
215: public final void close() throws IOException {
216: // shouldn't be called - we filter it out in writer
217: throw new IOException("close() called - shouldn't happen ");
218: }
219:
220: public final int read(byte cbuf[], int off, int len)
221: throws IOException {
222: if (pos >= end)
223: return -1;
224: if (pos + len > end) {
225: len = end - pos;
226: }
227: if (len <= 0) {
228: return 0;
229: }
230: System.arraycopy(buf, pos, cbuf, off, len);
231: pos += len;
232: return len;
233: }
234:
235: public final int read() throws IOException {
236: return (pos < end) ? (buf[pos++] & 0xff) : -1;
237: }
238:
239: // -------------------- Internal methods --------------------
240:
241: void setBuffer(byte b[], int p, int l) {
242: buf = b;
243: pos = p;
244: len = l;
245: end = pos + len;
246: }
247:
248: void setByteChunk(ByteChunk mb) {
249: buf = mb.getBytes();
250: pos = mb.getStart();
251: len = mb.getLength();
252: end = pos + len;
253: }
254:
255: }
|