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 ctx.setMatchCount(decodeAuto(in, ctx.getBuffer(), ctx
113 .getMatchCount(), ctx.getDecoder(), out));
114 } else {
115 ctx.setMatchCount(decodeNormal(in, ctx.getBuffer(), ctx
116 .getMatchCount(), ctx.getDecoder(), out));
117 }
118 }
119
120 private Context getContext(IoSession session) {
121 Context 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 int decodeAuto(ByteBuffer in, ByteBuffer buf, int matchCount,
142 CharsetDecoder decoder, ProtocolDecoderOutput out)
143 throws CharacterCodingException {
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 buf.put(in);
172 if (buf.position() > maxLineLength) {
173 throw new BufferDataException("Line is too long: "
174 + buf.position());
175 }
176 buf.flip();
177 buf.limit(buf.limit() - matchCount);
178 out.write(buf.getString(decoder));
179 buf.clear();
180
181 in.limit(oldLimit);
182 in.position(pos);
183 oldPos = pos;
184 matchCount = 0;
185 }
186 }
187
188
189 in.position(oldPos);
190 buf.put(in);
191
192 return matchCount;
193 }
194
195 private int decodeNormal(ByteBuffer in, ByteBuffer buf, int matchCount,
196 CharsetDecoder decoder, ProtocolDecoderOutput out)
197 throws CharacterCodingException {
198
199 if (delimBuf == null) {
200 ByteBuffer tmp = ByteBuffer.allocate(2).setAutoExpand(true);
201 tmp.putString(delimiter.getValue(), charset.newEncoder());
202 tmp.flip();
203 delimBuf = tmp;
204 }
205
206
207 int oldPos = in.position();
208 int oldLimit = in.limit();
209 while (in.hasRemaining()) {
210 byte b = in.get();
211 if (delimBuf.get(matchCount) == b) {
212 matchCount++;
213 if (matchCount == delimBuf.limit()) {
214
215 int pos = in.position();
216 in.limit(pos);
217 in.position(oldPos);
218
219 buf.put(in);
220 if (buf.position() > maxLineLength) {
221 throw new BufferDataException("Line is too long: "
222 + buf.position());
223 }
224 buf.flip();
225 buf.limit(buf.limit() - matchCount);
226 out.write(buf.getString(decoder));
227 buf.clear();
228
229 in.limit(oldLimit);
230 in.position(pos);
231 oldPos = pos;
232 matchCount = 0;
233 }
234 } else {
235 matchCount = 0;
236 }
237 }
238
239
240 in.position(oldPos);
241 buf.put(in);
242
243 return matchCount;
244 }
245
246 private class Context {
247 private final CharsetDecoder decoder;
248
249 private final ByteBuffer buf;
250
251 private int matchCount = 0;
252
253 private Context() {
254 decoder = charset.newDecoder();
255 buf = ByteBuffer.allocate(80).setAutoExpand(true);
256 }
257
258 public CharsetDecoder getDecoder() {
259 return decoder;
260 }
261
262 public ByteBuffer getBuffer() {
263 return buf;
264 }
265
266 public int getMatchCount() {
267 return matchCount;
268 }
269
270 public void setMatchCount(int matchCount) {
271 this.matchCount = matchCount;
272 }
273 }
274 }