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