1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.mina.filter.codec.textline;
20
21 import java.nio.charset.CharacterCodingException;
22 import java.nio.charset.Charset;
23 import java.nio.charset.CharsetDecoder;
24
25 import org.apache.mina.common.BufferDataException;
26 import org.apache.mina.common.ByteBuffer;
27 import org.apache.mina.common.IoSession;
28 import org.apache.mina.filter.codec.ProtocolDecoder;
29 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
30
31 /***
32 * A {@link ProtocolDecoder} which decodes a text line into a string.
33 *
34 * @author The Apache Directory Project (dev@directory.apache.org)
35 * @version $Rev: 355016 $, $Date: 2005-12-08 16:00:30 +0900 (Thu, 08 Dec 2005) $,
36 */
37 public class TextLineDecoder implements ProtocolDecoder
38 {
39 private static final String CONTEXT = TextLineDecoder.class.getName() + ".context";
40
41 private final Charset charset;
42 private final LineDelimiter delimiter;
43 private ByteBuffer delimBuf;
44 private int maxLineLength = 1024;
45
46 /***
47 * Creates a new instance with the current default {@link Charset}
48 * and {@link LineDelimiter#AUTO} delimiter.
49 */
50 public TextLineDecoder()
51 {
52 this( Charset.defaultCharset(), LineDelimiter.AUTO );
53 }
54
55 /***
56 * Creates a new instance with the spcified <tt>charset</tt>
57 * and {@link LineDelimiter#AUTO} delimiter.
58 */
59 public TextLineDecoder( Charset charset )
60 {
61 this( charset, LineDelimiter.AUTO );
62 }
63
64 /***
65 * Creates a new instance with the specified <tt>charset</tt>
66 * and the specified <tt>delimiter</tt>.
67 */
68 public TextLineDecoder( Charset charset, LineDelimiter delimiter )
69 {
70 if( charset == null )
71 {
72 throw new NullPointerException( "charset" );
73 }
74 if( delimiter == null )
75 {
76 throw new NullPointerException( "delimiter" );
77 }
78
79 this.charset = charset;
80 this.delimiter = delimiter;
81 }
82
83 /***
84 * Returns the allowed maximum size of the line to be decoded.
85 * If the size of the line to be decoded exceeds this value, the
86 * decoder will throw a {@link BufferDataException}. The default
87 * value is <tt>1024</tt> (1KB).
88 */
89 public int getMaxLineLength()
90 {
91 return maxLineLength;
92 }
93
94 /***
95 * Sets the allowed maximum size of the line to be decoded.
96 * If the size of the line to be decoded exceeds this value, the
97 * decoder will throw a {@link BufferDataException}. The default
98 * value is <tt>1024</tt> (1KB).
99 */
100 public void setMaxLineLength( int maxLineLength )
101 {
102 if( maxLineLength <= 0 )
103 {
104 throw new IllegalArgumentException( "maxLineLength: " + maxLineLength );
105 }
106
107 this.maxLineLength = maxLineLength;
108 }
109
110 public void decode( IoSession session, ByteBuffer in,
111 ProtocolDecoderOutput out )
112 throws Exception
113 {
114 Context ctx = ( Context ) session.getAttribute( CONTEXT );
115 if( ctx == null )
116 {
117 ctx = new Context();
118 session.setAttribute( CONTEXT, ctx );
119 }
120
121 if( LineDelimiter.AUTO.equals( delimiter ) )
122 {
123 ctx.setMatchCount(
124 decodeAuto(
125 in,
126 ctx.getBuffer(),
127 ctx.getMatchCount(),
128 ctx.getDecoder(),
129 out ) );
130 }
131 else
132 {
133 ctx.setMatchCount(
134 decodeNormal(
135 in,
136 ctx.getBuffer(),
137 ctx.getMatchCount(),
138 ctx.getDecoder(),
139 out ) );
140 }
141 }
142
143 public void dispose( IoSession session ) throws Exception
144 {
145 Context ctx = ( Context ) session.getAttribute( CONTEXT );
146 if( ctx != null )
147 {
148 ctx.getBuffer().release();
149 session.removeAttribute( CONTEXT );
150 }
151 }
152
153 private int decodeAuto( ByteBuffer in, ByteBuffer buf, int matchCount, CharsetDecoder decoder, ProtocolDecoderOutput out ) throws CharacterCodingException
154 {
155
156 int oldMatchCount = matchCount;
157 int oldPos = in.position();
158 int oldLimit = in.limit();
159 while( in.hasRemaining() )
160 {
161 byte b = in.get();
162 boolean matched = false;
163 switch( b )
164 {
165 case '\r':
166
167
168 matchCount ++;
169 break;
170 case '\n':
171
172 matchCount ++;
173 matched = true;
174 break;
175 default:
176 matchCount = 0;
177 }
178
179 if( matched )
180 {
181
182 int pos = in.position();
183 in.limit( pos - matchCount + oldMatchCount );
184 in.position( oldPos );
185
186 buf.put( in );
187 if( buf.position() > maxLineLength )
188 {
189 throw new BufferDataException( "Line is too long: " + buf.position() );
190 }
191 buf.flip();
192 out.write( buf.getString( decoder ) );
193 buf.clear();
194
195 in.limit( oldLimit );
196 in.position( pos );
197 oldPos = pos;
198 matchCount = 0;
199 }
200 }
201
202
203 in.position( oldPos );
204 in.limit( in.limit() - matchCount + oldMatchCount );
205 buf.put( in );
206
207 return matchCount;
208 }
209
210 private int decodeNormal( ByteBuffer in, ByteBuffer buf, int matchCount, CharsetDecoder decoder, ProtocolDecoderOutput out ) throws CharacterCodingException
211 {
212
213 if( delimBuf == null )
214 {
215 ByteBuffer tmp = ByteBuffer.allocate( 2 ).setAutoExpand( true );
216 tmp.putString( delimiter.getValue(), charset.newEncoder() );
217 tmp.flip();
218 delimBuf = tmp;
219 }
220
221
222 int oldMatchCount = matchCount;
223 int oldPos = in.position();
224 int oldLimit = in.limit();
225 while( in.hasRemaining() )
226 {
227 byte b = in.get();
228 if( delimBuf.get( matchCount ) == b )
229 {
230 matchCount ++;
231 if( matchCount == delimBuf.limit() )
232 {
233
234 int pos = in.position();
235 in.limit( pos - matchCount + oldMatchCount );
236 in.position( oldPos );
237
238 buf.put( in );
239 if( buf.position() > maxLineLength )
240 {
241 throw new BufferDataException( "Line is too long: " + buf.position() );
242 }
243 buf.flip();
244 out.write( buf.getString( decoder ) );
245 buf.clear();
246
247 in.limit( oldLimit );
248 in.position( pos );
249 oldPos = pos;
250 matchCount = 0;
251 }
252 }
253 else
254 {
255 matchCount = 0;
256 }
257 }
258
259
260 in.position( oldPos );
261 in.limit( in.limit() - matchCount + oldMatchCount );
262 buf.put( in );
263
264 return matchCount;
265 }
266
267 private class Context
268 {
269 private final CharsetDecoder decoder;
270 private final ByteBuffer buf;
271 private int matchCount = 0;
272
273 private Context()
274 {
275 decoder = charset.newDecoder();
276 buf = ByteBuffer.allocate( 80 ).setAutoExpand( true );
277 }
278
279 public CharsetDecoder getDecoder()
280 {
281 return decoder;
282 }
283
284 public ByteBuffer getBuffer()
285 {
286 return buf;
287 }
288
289 public int getMatchCount()
290 {
291 return matchCount;
292 }
293
294 public void setMatchCount( int matchCount )
295 {
296 this.matchCount = matchCount;
297 }
298 }
299 }