KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > buf > C2BConverter


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.tomcat.util.buf;
19
20 import java.io.IOException JavaDoc;
21 import java.io.OutputStream JavaDoc;
22 import java.io.OutputStreamWriter JavaDoc;
23 import java.io.UnsupportedEncodingException JavaDoc;
24
25 /** Efficient conversion of character to bytes.
26  *
27  * This uses the standard JDK mechansim - a writer - but provides mechanisms
28  * to recycle all the objects that are used. It is compatible with JDK1.1 and up,
29  * ( nio is better, but it's not available even in 1.2 or 1.3 )
30  *
31  */

32 public final class C2BConverter {
33     
34     private static org.apache.commons.logging.Log log=
35         org.apache.commons.logging.LogFactory.getLog(C2BConverter.class );
36     
37     private IntermediateOutputStream ios;
38     private WriteConvertor conv;
39     private ByteChunk bb;
40     private String JavaDoc enc;
41     
42     /** Create a converter, with bytes going to a byte buffer
43      */

44     public C2BConverter(ByteChunk output, String JavaDoc encoding) throws IOException JavaDoc {
45     this.bb=output;
46     ios=new IntermediateOutputStream( output );
47     conv=new WriteConvertor( ios, encoding );
48         this.enc=encoding;
49     }
50
51     /** Create a converter
52      */

53     public C2BConverter(String JavaDoc encoding) throws IOException JavaDoc {
54     this( new ByteChunk(1024), encoding );
55     }
56
57     public ByteChunk getByteChunk() {
58     return bb;
59     }
60
61     public String JavaDoc getEncoding() {
62         return enc;
63     }
64
65     public void setByteChunk(ByteChunk bb) {
66     this.bb=bb;
67     ios.setByteChunk( bb );
68     }
69
70     /** Reset the internal state, empty the buffers.
71      * The encoding remain in effect, the internal buffers remain allocated.
72      */

73     public final void recycle() {
74     conv.recycle();
75     bb.recycle();
76     }
77
78     /** Generate the bytes using the specified encoding
79      */

80     public final void convert(char c[], int off, int len ) throws IOException JavaDoc {
81     conv.write( c, off, len );
82     }
83
84     /** Generate the bytes using the specified encoding
85      */

86     public final void convert(String JavaDoc s, int off, int len ) throws IOException JavaDoc {
87     conv.write( s, off, len );
88     }
89
90     /** Generate the bytes using the specified encoding
91      */

92     public final void convert(String JavaDoc s ) throws IOException JavaDoc {
93     conv.write( s );
94     }
95
96     /** Generate the bytes using the specified encoding
97      */

98     public final void convert(char c ) throws IOException JavaDoc {
99     conv.write( c );
100     }
101
102     /** Convert a message bytes chars to bytes
103      */

104     public final void convert(MessageBytes mb ) throws IOException JavaDoc {
105         int type=mb.getType();
106         if( type==MessageBytes.T_BYTES )
107             return;
108         ByteChunk orig=bb;
109         setByteChunk( mb.getByteChunk());
110         bb.recycle();
111         bb.allocate( 32, -1 );
112         
113         if( type==MessageBytes.T_STR ) {
114             convert( mb.getString() );
115             // System.out.println("XXX Converting " + mb.getString() );
116
} else if( type==MessageBytes.T_CHARS ) {
117             CharChunk charC=mb.getCharChunk();
118             convert( charC.getBuffer(),
119                                 charC.getOffset(), charC.getLength());
120             //System.out.println("XXX Converting " + mb.getCharChunk() );
121
} else {
122             if (log.isDebugEnabled())
123                 log.debug("XXX unknowon type " + type );
124         }
125         flushBuffer();
126         //System.out.println("C2B: XXX " + bb.getBuffer() + bb.getLength());
127
setByteChunk(orig);
128     }
129
130     /** Flush any internal buffers into the ByteOutput or the internal
131      * byte[]
132      */

133     public final void flushBuffer() throws IOException JavaDoc {
134     conv.flush();
135     }
136
137 }
138
139 // -------------------- Private implementation --------------------
140

141
142
143 /**
144  * Special writer class, where close() is overritten. The default implementation
145  * would set byteOutputter to null, and the writter can't be recycled.
146  *
147  * Note that the flush method will empty the internal buffers _and_ call
148  * flush on the output stream - that's why we use an intermediary output stream
149  * that overrides flush(). The idea is to have full control: flushing the
150  * char->byte converter should be independent of flushing the OutputStream.
151  *
152  * When a WriteConverter is created, it'll allocate one or 2 byte buffers,
153  * with a 8k size that can't be changed ( at least in JDK1.1 -> 1.4 ). It would
154  * also allocate a ByteOutputter or equivalent - again some internal buffers.
155  *
156  * It is essential to keep this object around and reuse it. You can use either
157  * pools or per thread data - but given that in most cases a converter will be
158  * needed for every thread and most of the time only 1 ( or 2 ) encodings will
159  * be used, it is far better to keep it per thread and eliminate the pool
160  * overhead too.
161  *
162  */

163  final class WriteConvertor extends OutputStreamWriter JavaDoc {
164     // stream with flush() and close(). overriden.
165
private IntermediateOutputStream ios;
166     
167     // Has a private, internal byte[8192]
168

169     /** Create a converter.
170      */

171     public WriteConvertor( IntermediateOutputStream out, String JavaDoc enc )
172     throws UnsupportedEncodingException JavaDoc
173     {
174     super( out, enc );
175     ios=out;
176     }
177     
178     /** Overriden - will do nothing but reset internal state.
179      */

180     public final void close() throws IOException JavaDoc {
181     // NOTHING
182
// Calling super.close() would reset out and cb.
183
}
184     
185     /**
186      * Flush the characters only
187      */

188     public final void flush() throws IOException JavaDoc {
189     // Will flushBuffer and out()
190
// flushBuffer put any remaining chars in the byte[]
191
super.flush();
192     }
193     
194     public final void write(char cbuf[], int off, int len) throws IOException JavaDoc {
195     // will do the conversion and call write on the output stream
196
super.write( cbuf, off, len );
197     }
198     
199     /** Reset the buffer
200      */

201     public final void recycle() {
202     ios.disable();
203     try {
204         // System.out.println("Reseting writer");
205
flush();
206     } catch( Exception JavaDoc ex ) {
207         ex.printStackTrace();
208     }
209     ios.enable();
210     }
211     
212 }
213
214
215 /** Special output stream where close() is overriden, so super.close()
216     is never called.
217     
218     This allows recycling. It can also be disabled, so callbacks will
219     not be called if recycling the converter and if data was not flushed.
220 */

221 final class IntermediateOutputStream extends OutputStream JavaDoc {
222     private ByteChunk tbuff;
223     private boolean enabled=true;
224     
225     public IntermediateOutputStream(ByteChunk tbuff) {
226         this.tbuff=tbuff;
227     }
228     
229     public final void close() throws IOException JavaDoc {
230     // shouldn't be called - we filter it out in writer
231
throw new IOException JavaDoc("close() called - shouldn't happen ");
232     }
233     
234     public final void flush() throws IOException JavaDoc {
235     // nothing - write will go directly to the buffer,
236
// we don't keep any state
237
}
238     
239     public final void write(byte cbuf[], int off, int len) throws IOException JavaDoc {
240     // will do the conversion and call write on the output stream
241
if( enabled ) {
242         tbuff.append( cbuf, off, len );
243     }
244     }
245     
246     public final void write( int i ) throws IOException JavaDoc {
247     throw new IOException JavaDoc("write( int ) called - shouldn't happen ");
248     }
249
250     // -------------------- Internal methods --------------------
251

252     void setByteChunk( ByteChunk bb ) {
253     tbuff=bb;
254     }
255     
256     /** Temporary disable - this is used to recycle the converter without
257      * generating an output if the buffers were not flushed
258      */

259     final void disable() {
260     enabled=false;
261     }
262
263     /** Reenable - used to recycle the converter
264      */

265     final void enable() {
266     enabled=true;
267     }
268 }
269
Popular Tags