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.common.AttributeKey;
27 import org.apache.mina.common.BufferDataException;
28 import org.apache.mina.common.IoBuffer;
29 import org.apache.mina.common.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, in, out);
139 } else {
140 decodeNormal(ctx, 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, 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 out.write(buf.getString(ctx.getDecoder()));
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, 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 out.write(buf.getString(ctx.getDecoder()));
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(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 private class Context {
294 private final CharsetDecoder decoder;
295 private final IoBuffer buf;
296 private int matchCount = 0;
297 private int overflowPosition = 0;
298
299 private Context() {
300 decoder = charset.newDecoder();
301 buf = IoBuffer.allocate(80).setAutoExpand(true);
302 }
303
304 public CharsetDecoder getDecoder() {
305 return decoder;
306 }
307
308 public IoBuffer getBuffer() {
309 return buf;
310 }
311
312 public int getOverflowPosition() {
313 return overflowPosition;
314 }
315
316 public int getMatchCount() {
317 return matchCount;
318 }
319
320 public void setMatchCount(int matchCount) {
321 this.matchCount = matchCount;
322 }
323
324 public void reset() {
325 overflowPosition = 0;
326 matchCount = 0;
327 decoder.reset();
328 }
329
330 public void append(IoBuffer in) {
331 if (overflowPosition != 0) {
332 discard(in);
333 } else if (buf.position() > maxLineLength - in.remaining()) {
334 overflowPosition = buf.position();
335 buf.clear();
336 discard(in);
337 } else {
338 getBuffer().put(in);
339 }
340 }
341
342 private void discard(IoBuffer in) {
343 if (Integer.MAX_VALUE - in.remaining() < overflowPosition) {
344 overflowPosition = Integer.MAX_VALUE;
345 } else {
346 overflowPosition += in.remaining();
347 }
348 in.position(in.limit());
349 }
350 }
351 }