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
41 public class TextLineDecoder implements ProtocolDecoder {
42 private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
43
44 private final Charset charset;
45
46 private final LineDelimiter delimiter;
47
48 private IoBuffer delimBuf;
49
50 private int maxLineLength = 1024;
51
52
53
54
55
56 public TextLineDecoder() {
57 this(LineDelimiter.AUTO);
58 }
59
60
61
62
63
64 public TextLineDecoder(String delimiter) {
65 this(new LineDelimiter(delimiter));
66 }
67
68
69
70
71
72 public TextLineDecoder(LineDelimiter delimiter) {
73 this(Charset.defaultCharset(), delimiter);
74 }
75
76
77
78
79
80 public TextLineDecoder(Charset charset) {
81 this(charset, LineDelimiter.AUTO);
82 }
83
84
85
86
87
88 public TextLineDecoder(Charset charset, String delimiter) {
89 this(charset, new LineDelimiter(delimiter));
90 }
91
92
93
94
95
96 public TextLineDecoder(Charset charset, LineDelimiter delimiter) {
97 if (charset == null) {
98 throw new NullPointerException("charset");
99 }
100 if (delimiter == null) {
101 throw new NullPointerException("delimiter");
102 }
103
104 this.charset = charset;
105 this.delimiter = delimiter;
106 }
107
108
109
110
111
112
113
114 public int getMaxLineLength() {
115 return maxLineLength;
116 }
117
118
119
120
121
122
123
124 public void setMaxLineLength(int maxLineLength) {
125 if (maxLineLength <= 0) {
126 throw new IllegalArgumentException("maxLineLength: "
127 + maxLineLength);
128 }
129
130 this.maxLineLength = maxLineLength;
131 }
132
133 public void decode(IoSession session, IoBuffer in,
134 ProtocolDecoderOutput out) throws Exception {
135 Context ctx = getContext(session);
136
137 if (LineDelimiter.AUTO.equals(delimiter)) {
138 decodeAuto(ctx, session, in, out);
139 } else {
140 decodeNormal(ctx, session, in, out);
141 }
142 }
143
144 private Context getContext(IoSession session) {
145 Context ctx;
146 ctx = (Context) session.getAttribute(CONTEXT);
147 if (ctx == null) {
148 ctx = new Context();
149 session.setAttribute(CONTEXT, ctx);
150 }
151 return ctx;
152 }
153
154 public void finishDecode(IoSession session, ProtocolDecoderOutput out)
155 throws Exception {
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 }