1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.codec.net;
19
20 import java.io.UnsupportedEncodingException;
21 import java.nio.charset.Charset;
22 import java.util.BitSet;
23
24 import org.apache.commons.codec.Charsets;
25 import org.apache.commons.codec.DecoderException;
26 import org.apache.commons.codec.EncoderException;
27 import org.apache.commons.codec.StringDecoder;
28 import org.apache.commons.codec.StringEncoder;
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52 public class QCodec extends RFC1522Codec implements StringEncoder, StringDecoder {
53
54
55
56 private final Charset charset;
57
58
59
60
61 private static final BitSet PRINTABLE_CHARS = new BitSet(256);
62
63 static {
64
65 PRINTABLE_CHARS.set(' ');
66 PRINTABLE_CHARS.set('!');
67 PRINTABLE_CHARS.set('"');
68 PRINTABLE_CHARS.set('#');
69 PRINTABLE_CHARS.set('$');
70 PRINTABLE_CHARS.set('%');
71 PRINTABLE_CHARS.set('&');
72 PRINTABLE_CHARS.set('\'');
73 PRINTABLE_CHARS.set('(');
74 PRINTABLE_CHARS.set(')');
75 PRINTABLE_CHARS.set('*');
76 PRINTABLE_CHARS.set('+');
77 PRINTABLE_CHARS.set(',');
78 PRINTABLE_CHARS.set('-');
79 PRINTABLE_CHARS.set('.');
80 PRINTABLE_CHARS.set('/');
81 for (int i = '0'; i <= '9'; i++) {
82 PRINTABLE_CHARS.set(i);
83 }
84 PRINTABLE_CHARS.set(':');
85 PRINTABLE_CHARS.set(';');
86 PRINTABLE_CHARS.set('<');
87 PRINTABLE_CHARS.set('>');
88 PRINTABLE_CHARS.set('@');
89 for (int i = 'A'; i <= 'Z'; i++) {
90 PRINTABLE_CHARS.set(i);
91 }
92 PRINTABLE_CHARS.set('[');
93 PRINTABLE_CHARS.set('\\');
94 PRINTABLE_CHARS.set(']');
95 PRINTABLE_CHARS.set('^');
96 PRINTABLE_CHARS.set('`');
97 for (int i = 'a'; i <= 'z'; i++) {
98 PRINTABLE_CHARS.set(i);
99 }
100 PRINTABLE_CHARS.set('{');
101 PRINTABLE_CHARS.set('|');
102 PRINTABLE_CHARS.set('}');
103 PRINTABLE_CHARS.set('~');
104 }
105
106 private static final byte BLANK = 32;
107
108 private static final byte UNDERSCORE = 95;
109
110 private boolean encodeBlanks = false;
111
112
113
114
115 public QCodec() {
116 this(Charsets.UTF_8);
117 }
118
119
120
121
122
123
124
125
126
127
128 public QCodec(final Charset charset) {
129 super();
130 this.charset = charset;
131 }
132
133
134
135
136
137
138
139
140
141
142
143 public QCodec(final String charsetName) {
144 this(Charset.forName(charsetName));
145 }
146
147 @Override
148 protected String getEncoding() {
149 return "Q";
150 }
151
152 @Override
153 protected byte[] doEncoding(byte[] bytes) {
154 if (bytes == null) {
155 return null;
156 }
157 byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(PRINTABLE_CHARS, bytes);
158 if (this.encodeBlanks) {
159 for (int i = 0; i < data.length; i++) {
160 if (data[i] == BLANK) {
161 data[i] = UNDERSCORE;
162 }
163 }
164 }
165 return data;
166 }
167
168 @Override
169 protected byte[] doDecoding(byte[] bytes) throws DecoderException {
170 if (bytes == null) {
171 return null;
172 }
173 boolean hasUnderscores = false;
174 for (byte b : bytes) {
175 if (b == UNDERSCORE) {
176 hasUnderscores = true;
177 break;
178 }
179 }
180 if (hasUnderscores) {
181 byte[] tmp = new byte[bytes.length];
182 for (int i = 0; i < bytes.length; i++) {
183 byte b = bytes[i];
184 if (b != UNDERSCORE) {
185 tmp[i] = b;
186 } else {
187 tmp[i] = BLANK;
188 }
189 }
190 return QuotedPrintableCodec.decodeQuotedPrintable(tmp);
191 }
192 return QuotedPrintableCodec.decodeQuotedPrintable(bytes);
193 }
194
195
196
197
198
199
200
201
202
203
204
205
206
207 public String encode(final String str, final Charset charset) throws EncoderException {
208 if (str == null) {
209 return null;
210 }
211 return encodeText(str, charset);
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225 public String encode(final String str, final String charset) throws EncoderException {
226 if (str == null) {
227 return null;
228 }
229 try {
230 return encodeText(str, charset);
231 } catch (UnsupportedEncodingException e) {
232 throw new EncoderException(e.getMessage(), e);
233 }
234 }
235
236
237
238
239
240
241
242
243
244
245 @Override
246 public String encode(String str) throws EncoderException {
247 if (str == null) {
248 return null;
249 }
250 return encode(str, getCharset());
251 }
252
253
254
255
256
257
258
259
260
261
262
263 @Override
264 public String decode(String str) throws DecoderException {
265 if (str == null) {
266 return null;
267 }
268 try {
269 return decodeText(str);
270 } catch (UnsupportedEncodingException e) {
271 throw new DecoderException(e.getMessage(), e);
272 }
273 }
274
275
276
277
278
279
280
281
282
283
284 @Override
285 public Object encode(Object obj) throws EncoderException {
286 if (obj == null) {
287 return null;
288 } else if (obj instanceof String) {
289 return encode((String) obj);
290 } else {
291 throw new EncoderException("Objects of type " +
292 obj.getClass().getName() +
293 " cannot be encoded using Q codec");
294 }
295 }
296
297
298
299
300
301
302
303
304
305
306
307
308 @Override
309 public Object decode(Object obj) throws DecoderException {
310 if (obj == null) {
311 return null;
312 } else if (obj instanceof String) {
313 return decode((String) obj);
314 } else {
315 throw new DecoderException("Objects of type " +
316 obj.getClass().getName() +
317 " cannot be decoded using Q codec");
318 }
319 }
320
321
322
323
324
325
326
327 public Charset getCharset() {
328 return this.charset;
329 }
330
331
332
333
334
335
336 public String getDefaultCharset() {
337 return this.charset.name();
338 }
339
340
341
342
343
344
345 public boolean isEncodeBlanks() {
346 return this.encodeBlanks;
347 }
348
349
350
351
352
353
354
355 public void setEncodeBlanks(boolean b) {
356 this.encodeBlanks = b;
357 }
358 }