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
256 in.position(Math.max(0, in.position() - matchCount));
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 }