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