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