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.charset.CharacterCodingException;
23 import java.nio.charset.Charset;
24 import java.nio.charset.CharsetDecoder;
25
26 import org.apache.mina.core.buffer.BufferDataException;
27 import org.apache.mina.core.buffer.IoBuffer;
28 import org.apache.mina.core.session.AttributeKey;
29 import org.apache.mina.core.session.IoSession;
30 import org.apache.mina.filter.codec.ProtocolDecoder;
31 import org.apache.mina.filter.codec.ProtocolDecoderException;
32 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
33 import org.apache.mina.filter.codec.RecoverableProtocolDecoderException;
34
35
36
37
38
39
40 public class TextLineDecoder implements ProtocolDecoder {
41 private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
42
43 private final Charset charset;
44
45 private final LineDelimiter delimiter;
46
47 private IoBuffer delimBuf;
48
49 private int maxLineLength = 1024;
50
51
52
53
54
55 public TextLineDecoder() {
56 this(LineDelimiter.AUTO);
57 }
58
59
60
61
62
63 public TextLineDecoder(String delimiter) {
64 this(new LineDelimiter(delimiter));
65 }
66
67
68
69
70
71 public TextLineDecoder(LineDelimiter delimiter) {
72 this(Charset.defaultCharset(), delimiter);
73 }
74
75
76
77
78
79 public TextLineDecoder(Charset charset) {
80 this(charset, LineDelimiter.AUTO);
81 }
82
83
84
85
86
87 public TextLineDecoder(Charset charset, String delimiter) {
88 this(charset, new LineDelimiter(delimiter));
89 }
90
91
92
93
94
95 public TextLineDecoder(Charset charset, LineDelimiter delimiter) {
96 if (charset == null) {
97 throw new NullPointerException("charset");
98 }
99 if (delimiter == null) {
100 throw new NullPointerException("delimiter");
101 }
102
103 this.charset = charset;
104 this.delimiter = delimiter;
105 }
106
107
108
109
110
111
112
113 public int getMaxLineLength() {
114 return maxLineLength;
115 }
116
117
118
119
120
121
122
123 public void setMaxLineLength(int maxLineLength) {
124 if (maxLineLength <= 0) {
125 throw new IllegalArgumentException("maxLineLength: "
126 + maxLineLength);
127 }
128
129 this.maxLineLength = maxLineLength;
130 }
131
132 public void decode(IoSession session, IoBuffer in,
133 ProtocolDecoderOutput out) throws Exception {
134 Context ctx = getContext(session);
135
136 if (LineDelimiter.AUTO.equals(delimiter)) {
137 decodeAuto(ctx, session, in, out);
138 } else {
139 decodeNormal(ctx, session, in, out);
140 }
141 }
142
143 private Context getContext(IoSession session) {
144 Context ctx;
145 ctx = (Context) session.getAttribute(CONTEXT);
146 if (ctx == null) {
147 ctx = new Context();
148 session.setAttribute(CONTEXT, ctx);
149 }
150 return ctx;
151 }
152
153 public void finishDecode(IoSession session, ProtocolDecoderOutput out)
154 throws Exception {
155
156 }
157
158 public void dispose(IoSession session) throws Exception {
159 Context ctx = (Context) session.getAttribute(CONTEXT);
160 if (ctx != null) {
161 session.removeAttribute(CONTEXT);
162 }
163 }
164
165 private void decodeAuto(Context ctx, IoSession session, IoBuffer in, ProtocolDecoderOutput out)
166 throws CharacterCodingException, ProtocolDecoderException {
167
168 int matchCount = ctx.getMatchCount();
169
170
171 int oldPos = in.position();
172 int oldLimit = in.limit();
173 while (in.hasRemaining()) {
174 byte b = in.get();
175 boolean matched = false;
176 switch (b) {
177 case '\r':
178
179
180 matchCount++;
181 break;
182 case '\n':
183
184 matchCount++;
185 matched = true;
186 break;
187 default:
188 matchCount = 0;
189 }
190
191 if (matched) {
192
193 int pos = in.position();
194 in.limit(pos);
195 in.position(oldPos);
196
197 ctx.append(in);
198
199 in.limit(oldLimit);
200 in.position(pos);
201
202 if (ctx.getOverflowPosition() == 0) {
203 IoBuffer buf = ctx.getBuffer();
204 buf.flip();
205 buf.limit(buf.limit() - matchCount);
206 try {
207 writeText(session, buf.getString(ctx.getDecoder()), out);
208 } finally {
209 buf.clear();
210 }
211 } else {
212 int overflowPosition = ctx.getOverflowPosition();
213 ctx.reset();
214 throw new RecoverableProtocolDecoderException(
215 "Line is too long: " + overflowPosition);
216 }
217
218 oldPos = pos;
219 matchCount = 0;
220 }
221 }
222
223
224 in.position(oldPos);
225 ctx.append(in);
226
227 ctx.setMatchCount(matchCount);
228 }
229
230 private void decodeNormal(Context ctx, IoSession session, IoBuffer in, ProtocolDecoderOutput out)
231 throws CharacterCodingException, ProtocolDecoderException {
232
233 int matchCount = ctx.getMatchCount();
234
235
236 if (delimBuf == null) {
237 IoBuffer tmp = IoBuffer.allocate(2).setAutoExpand(true);
238 tmp.putString(delimiter.getValue(), charset.newEncoder());
239 tmp.flip();
240 delimBuf = tmp;
241 }
242
243
244 int oldPos = in.position();
245 int oldLimit = in.limit();
246 while (in.hasRemaining()) {
247 byte b = in.get();
248 if (delimBuf.get(matchCount) == b) {
249 matchCount++;
250 if (matchCount == delimBuf.limit()) {
251
252 int pos = in.position();
253 in.limit(pos);
254 in.position(oldPos);
255
256 ctx.append(in);
257
258 in.limit(oldLimit);
259 in.position(pos);
260 if (ctx.getOverflowPosition() == 0) {
261 IoBuffer buf = ctx.getBuffer();
262 buf.flip();
263 buf.limit(buf.limit() - matchCount);
264 try {
265 writeText(session, buf.getString(ctx.getDecoder()), out);
266 } finally {
267 buf.clear();
268 }
269 } else {
270 int overflowPosition = ctx.getOverflowPosition();
271 ctx.reset();
272 throw new RecoverableProtocolDecoderException(
273 "Line is too long: " + overflowPosition);
274 }
275
276 oldPos = pos;
277 matchCount = 0;
278 }
279 } else {
280
281 in.position(Math.max(0, in.position() - matchCount));
282 matchCount = 0;
283 }
284 }
285
286
287 in.position(oldPos);
288 ctx.append(in);
289
290 ctx.setMatchCount(matchCount);
291 }
292
293
294
295
296
297
298
299
300
301
302 protected void writeText(IoSession session, String text, ProtocolDecoderOutput out) {
303 out.write(text);
304 }
305
306 private class Context {
307 private final CharsetDecoder decoder;
308 private final IoBuffer buf;
309 private int matchCount = 0;
310 private int overflowPosition = 0;
311
312 private Context() {
313 decoder = charset.newDecoder();
314 buf = IoBuffer.allocate(80).setAutoExpand(true);
315 }
316
317 public CharsetDecoder getDecoder() {
318 return decoder;
319 }
320
321 public IoBuffer getBuffer() {
322 return buf;
323 }
324
325 public int getOverflowPosition() {
326 return overflowPosition;
327 }
328
329 public int getMatchCount() {
330 return matchCount;
331 }
332
333 public void setMatchCount(int matchCount) {
334 this.matchCount = matchCount;
335 }
336
337 public void reset() {
338 overflowPosition = 0;
339 matchCount = 0;
340 decoder.reset();
341 }
342
343 public void append(IoBuffer in) {
344 if (overflowPosition != 0) {
345 discard(in);
346 } else if (buf.position() > maxLineLength - in.remaining()) {
347 overflowPosition = buf.position();
348 buf.clear();
349 discard(in);
350 } else {
351 getBuffer().put(in);
352 }
353 }
354
355 private void discard(IoBuffer in) {
356 if (Integer.MAX_VALUE - in.remaining() < overflowPosition) {
357 overflowPosition = Integer.MAX_VALUE;
358 } else {
359 overflowPosition += in.remaining();
360 }
361 in.position(in.limit());
362 }
363 }
364 }