1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.codec.textline;
21
22 import java.nio.ByteBuffer;
23 import java.nio.CharBuffer;
24 import java.nio.charset.CharacterCodingException;
25 import java.nio.charset.Charset;
26 import java.nio.charset.CharsetDecoder;
27
28 import org.apache.mina.core.buffer.BufferDataException;
29 import org.apache.mina.core.buffer.IoBuffer;
30 import org.apache.mina.core.session.AttributeKey;
31 import org.apache.mina.core.session.IoSession;
32 import org.apache.mina.filter.codec.ProtocolDecoder;
33 import org.apache.mina.filter.codec.ProtocolDecoderException;
34 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
35 import org.apache.mina.filter.codec.RecoverableProtocolDecoderException;
36
37
38
39
40
41
42 public class TextLineDecoder implements ProtocolDecoder {
43 private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
44
45 private final Charset charset;
46
47
48 private final LineDelimiter delimiter;
49
50
51 private IoBuffer delimBuf;
52
53
54 private int maxLineLength = 1024;
55
56
57 private int bufferLength = 128;
58
59
60
61
62
63 public TextLineDecoder() {
64 this(LineDelimiter.AUTO);
65 }
66
67
68
69
70
71 public TextLineDecoder(String delimiter) {
72 this(new LineDelimiter(delimiter));
73 }
74
75
76
77
78
79 public TextLineDecoder(LineDelimiter delimiter) {
80 this(Charset.defaultCharset(), delimiter);
81 }
82
83
84
85
86
87 public TextLineDecoder(Charset charset) {
88 this(charset, LineDelimiter.AUTO);
89 }
90
91
92
93
94
95 public TextLineDecoder(Charset charset, String delimiter) {
96 this(charset, new LineDelimiter(delimiter));
97 }
98
99
100
101
102
103 public TextLineDecoder(Charset charset, LineDelimiter delimiter) {
104 if (charset == null) {
105 throw new IllegalArgumentException("charset parameter shuld not be null");
106 }
107
108 if (delimiter == null) {
109 throw new IllegalArgumentException("delimiter parameter should not be null");
110 }
111
112 this.charset = charset;
113 this.delimiter = delimiter;
114
115
116 if (delimBuf == null) {
117 IoBuffer tmp = IoBuffer.allocate(2).setAutoExpand(true);
118
119 try {
120 tmp.putString(delimiter.getValue(), charset.newEncoder());
121 } catch (CharacterCodingException cce) {
122
123 }
124
125 tmp.flip();
126 delimBuf = tmp;
127 }
128 }
129
130
131
132
133
134
135
136 public int getMaxLineLength() {
137 return maxLineLength;
138 }
139
140
141
142
143
144
145
146 public void setMaxLineLength(int maxLineLength) {
147 if (maxLineLength <= 0) {
148 throw new IllegalArgumentException("maxLineLength (" + maxLineLength + ") should be a positive value");
149 }
150
151 this.maxLineLength = maxLineLength;
152 }
153
154
155
156
157
158
159
160 public void setBufferLength(int bufferLength) {
161 if (bufferLength <= 0) {
162 throw new IllegalArgumentException("bufferLength (" + maxLineLength + ") should be a positive value");
163
164 }
165
166 this.bufferLength = bufferLength;
167 }
168
169
170
171
172
173 public int getBufferLength() {
174 return bufferLength;
175 }
176
177
178
179
180 public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
181 Context ctx = getContext(session);
182
183 if (LineDelimiter.AUTO.equals(delimiter)) {
184 decodeAuto(ctx, session, in, out);
185 } else {
186 decodeNormal(ctx, session, in, out);
187 }
188 }
189
190
191
192
193 private Context getContext(IoSession session) {
194 Context ctx;
195 ctx = (Context) session.getAttribute(CONTEXT);
196
197 if (ctx == null) {
198 ctx = new Context(bufferLength);
199 session.setAttribute(CONTEXT, ctx);
200 }
201
202 return ctx;
203 }
204
205
206
207
208 public void finishDecode(IoSession session, ProtocolDecoderOutput out) throws Exception {
209
210 }
211
212
213
214
215 public void dispose(IoSession session) throws Exception {
216 Context ctx = (Context) session.getAttribute(CONTEXT);
217
218 if (ctx != null) {
219 session.removeAttribute(CONTEXT);
220 }
221 }
222
223
224
225
226 private void decodeAuto(Context ctx, IoSession session, IoBuffer in, ProtocolDecoderOutput out)
227 throws CharacterCodingException, ProtocolDecoderException {
228 int matchCount = ctx.getMatchCount();
229
230
231 int oldPos = in.position();
232 int oldLimit = in.limit();
233
234 while (in.hasRemaining()) {
235 byte b = in.get();
236 boolean matched = false;
237
238 switch (b) {
239 case '\r':
240
241
242 matchCount++;
243 break;
244
245 case '\n':
246
247 matchCount++;
248 matched = true;
249 break;
250
251 default:
252 matchCount = 0;
253 }
254
255 if (matched) {
256
257 int pos = in.position();
258 in.limit(pos);
259 in.position(oldPos);
260
261 ctx.append(in);
262
263 in.limit(oldLimit);
264 in.position(pos);
265
266 if (ctx.getOverflowPosition() == 0) {
267 IoBuffer buf = ctx.getBuffer();
268 buf.flip();
269 buf.limit(buf.limit() - matchCount);
270
271 try {
272 byte[] data = new byte[buf.limit()];
273 buf.get(data);
274 CharsetDecoder decoder = ctx.getDecoder();
275
276 CharBuffer buffer = decoder.decode(ByteBuffer.wrap(data));
277 String str = new String(buffer.array());
278 writeText(session, str, out);
279 } finally {
280 buf.clear();
281 }
282 } else {
283 int overflowPosition = ctx.getOverflowPosition();
284 ctx.reset();
285 throw new RecoverableProtocolDecoderException("Line is too long: " + overflowPosition);
286 }
287
288 oldPos = pos;
289 matchCount = 0;
290 }
291 }
292
293
294 in.position(oldPos);
295 ctx.append(in);
296
297 ctx.setMatchCount(matchCount);
298 }
299
300
301
302
303 private void decodeNormal(Context ctx, IoSession session, IoBuffer in, ProtocolDecoderOutput out)
304 throws CharacterCodingException, ProtocolDecoderException {
305 int matchCount = ctx.getMatchCount();
306
307
308 int oldPos = in.position();
309 int oldLimit = in.limit();
310
311 while (in.hasRemaining()) {
312 byte b = in.get();
313
314 if (delimBuf.get(matchCount) == b) {
315 matchCount++;
316
317 if (matchCount == delimBuf.limit()) {
318
319 int pos = in.position();
320 in.limit(pos);
321 in.position(oldPos);
322
323 ctx.append(in);
324
325 in.limit(oldLimit);
326 in.position(pos);
327
328 if (ctx.getOverflowPosition() == 0) {
329 IoBuffer buf = ctx.getBuffer();
330 buf.flip();
331 buf.limit(buf.limit() - matchCount);
332
333 try {
334 writeText(session, buf.getString(ctx.getDecoder()), out);
335 } finally {
336 buf.clear();
337 }
338 } else {
339 int overflowPosition = ctx.getOverflowPosition();
340 ctx.reset();
341 throw new RecoverableProtocolDecoderException("Line is too long: " + overflowPosition);
342 }
343
344 oldPos = pos;
345 matchCount = 0;
346 }
347 } else {
348
349 in.position(Math.max(0, in.position() - matchCount));
350 matchCount = 0;
351 }
352 }
353
354
355 in.position(oldPos);
356 ctx.append(in);
357
358 ctx.setMatchCount(matchCount);
359 }
360
361
362
363
364
365
366
367
368
369
370 protected void writeText(IoSession session, String text, ProtocolDecoderOutput out) {
371 out.write(text);
372 }
373
374
375
376
377
378
379
380
381 private class Context {
382
383 private final CharsetDecoder decoder;
384
385
386 private final IoBuffer buf;
387
388
389 private int matchCount = 0;
390
391
392 private int overflowPosition = 0;
393
394
395 private Context(int bufferLength) {
396 decoder = charset.newDecoder();
397 buf = IoBuffer.allocate(bufferLength).setAutoExpand(true);
398 }
399
400 public CharsetDecoder getDecoder() {
401 return decoder;
402 }
403
404 public IoBuffer getBuffer() {
405 return buf;
406 }
407
408 public int getOverflowPosition() {
409 return overflowPosition;
410 }
411
412 public int getMatchCount() {
413 return matchCount;
414 }
415
416 public void setMatchCount(int matchCount) {
417 this.matchCount = matchCount;
418 }
419
420 public void reset() {
421 overflowPosition = 0;
422 matchCount = 0;
423 decoder.reset();
424 }
425
426 public void append(IoBuffer in) {
427 if (overflowPosition != 0) {
428 discard(in);
429 } else if (buf.position() > maxLineLength - in.remaining()) {
430 overflowPosition = buf.position();
431 buf.clear();
432 discard(in);
433 } else {
434 getBuffer().put(in);
435 }
436 }
437
438 private void discard(IoBuffer in) {
439 if (Integer.MAX_VALUE - in.remaining() < overflowPosition) {
440 overflowPosition = Integer.MAX_VALUE;
441 } else {
442 overflowPosition += in.remaining();
443 }
444
445 in.position(in.limit());
446 }
447 }
448 }