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