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 matchCount = 0;
258 }
259 }
260
261
262 in.position(oldPos);
263 ctx.append(in);
264
265 ctx.setMatchCount(matchCount);
266 }
267
268 private class Context {
269 private final CharsetDecoder decoder;
270 private final ByteBuffer buf;
271 private int matchCount = 0;
272 private int overflowPosition = 0;
273
274 private Context() {
275 decoder = charset.newDecoder();
276 buf = ByteBuffer.allocate(80).setAutoExpand(true);
277 }
278
279 public CharsetDecoder getDecoder() {
280 return decoder;
281 }
282
283 public ByteBuffer getBuffer() {
284 return buf;
285 }
286
287 public int getOverflowPosition() {
288 return overflowPosition;
289 }
290
291 public int getMatchCount() {
292 return matchCount;
293 }
294
295 public void setMatchCount(int matchCount) {
296 this.matchCount = matchCount;
297 }
298
299 public void reset() {
300 overflowPosition = 0;
301 matchCount = 0;
302 decoder.reset();
303 }
304
305 public void append(ByteBuffer in) {
306 if (overflowPosition != 0) {
307 discard(in);
308 } else if (buf.position() > maxLineLength - in.remaining()) {
309 overflowPosition = buf.position();
310 buf.clear();
311 discard(in);
312 } else {
313 getBuffer().put(in);
314 }
315 }
316
317 private void discard(ByteBuffer in) {
318 if (Integer.MAX_VALUE - in.remaining() < overflowPosition) {
319 overflowPosition = Integer.MAX_VALUE;
320 } else {
321 overflowPosition += in.remaining();
322 }
323 in.position(in.limit());
324 }
325 }
326 }