001: /*
002: * Copyright 2001-2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.commons.codec.net;
018:
019: import java.io.UnsupportedEncodingException;
020: import java.util.BitSet;
021:
022: import org.apache.commons.codec.DecoderException;
023: import org.apache.commons.codec.EncoderException;
024: import org.apache.commons.codec.StringDecoder;
025: import org.apache.commons.codec.StringEncoder;
026:
027: /**
028: * <p>
029: * Similar to the Quoted-Printable content-transfer-encoding defined in <a
030: * href="http://www.ietf.org/rfc/rfc1521.txt">RFC 1521</a> and designed to allow text containing mostly ASCII
031: * characters to be decipherable on an ASCII terminal without decoding.
032: * </p>
033: *
034: * <p>
035: * <a href="http://www.ietf.org/rfc/rfc1522.txt">RFC 1522</a> describes techniques to allow the encoding of non-ASCII
036: * text in various portions of a RFC 822 [2] message header, in a manner which is unlikely to confuse existing message
037: * handling software.
038: * </p>
039: *
040: * @see <a href="http://www.ietf.org/rfc/rfc1522.txt">MIME (Multipurpose Internet Mail Extensions) Part Two: Message
041: * Header Extensions for Non-ASCII Text</a>
042: *
043: * @author Apache Software Foundation
044: * @since 1.3
045: * @version $Id: QCodec.java,v 1.6 2004/05/24 00:24:32 ggregory Exp $
046: */
047: public class QCodec extends RFC1522Codec implements StringEncoder,
048: StringDecoder {
049: /**
050: * The default charset used for string decoding and encoding.
051: */
052: private String charset = StringEncodings.UTF8;
053:
054: /**
055: * BitSet of printable characters as defined in RFC 1522.
056: */
057: private static final BitSet PRINTABLE_CHARS = new BitSet(256);
058: // Static initializer for printable chars collection
059: static {
060: // alpha characters
061: PRINTABLE_CHARS.set(' ');
062: PRINTABLE_CHARS.set('!');
063: PRINTABLE_CHARS.set('"');
064: PRINTABLE_CHARS.set('#');
065: PRINTABLE_CHARS.set('$');
066: PRINTABLE_CHARS.set('%');
067: PRINTABLE_CHARS.set('&');
068: PRINTABLE_CHARS.set('\'');
069: PRINTABLE_CHARS.set('(');
070: PRINTABLE_CHARS.set(')');
071: PRINTABLE_CHARS.set('*');
072: PRINTABLE_CHARS.set('+');
073: PRINTABLE_CHARS.set(',');
074: PRINTABLE_CHARS.set('-');
075: PRINTABLE_CHARS.set('.');
076: PRINTABLE_CHARS.set('/');
077: for (int i = '0'; i <= '9'; i++) {
078: PRINTABLE_CHARS.set(i);
079: }
080: PRINTABLE_CHARS.set(':');
081: PRINTABLE_CHARS.set(';');
082: PRINTABLE_CHARS.set('<');
083: PRINTABLE_CHARS.set('>');
084: PRINTABLE_CHARS.set('@');
085: for (int i = 'A'; i <= 'Z'; i++) {
086: PRINTABLE_CHARS.set(i);
087: }
088: PRINTABLE_CHARS.set('[');
089: PRINTABLE_CHARS.set('\\');
090: PRINTABLE_CHARS.set(']');
091: PRINTABLE_CHARS.set('^');
092: PRINTABLE_CHARS.set('`');
093: for (int i = 'a'; i <= 'z'; i++) {
094: PRINTABLE_CHARS.set(i);
095: }
096: PRINTABLE_CHARS.set('{');
097: PRINTABLE_CHARS.set('|');
098: PRINTABLE_CHARS.set('}');
099: PRINTABLE_CHARS.set('~');
100: }
101:
102: private static byte BLANK = 32;
103:
104: private static byte UNDERSCORE = 95;
105:
106: private boolean encodeBlanks = false;
107:
108: /**
109: * Default constructor.
110: */
111: public QCodec() {
112: super ();
113: }
114:
115: /**
116: * Constructor which allows for the selection of a default charset
117: *
118: * @param charset
119: * the default string charset to use.
120: *
121: * @see <a href="http://java.sun.com/j2se/1.3/docs/api/java/lang/package-summary.html#charenc">JRE character
122: * encoding names</a>
123: */
124: public QCodec(final String charset) {
125: super ();
126: this .charset = charset;
127: }
128:
129: protected String getEncoding() {
130: return "Q";
131: }
132:
133: protected byte[] doEncoding(byte[] bytes) throws EncoderException {
134: if (bytes == null) {
135: return null;
136: }
137: byte[] data = QuotedPrintableCodec.encodeQuotedPrintable(
138: PRINTABLE_CHARS, bytes);
139: if (this .encodeBlanks) {
140: for (int i = 0; i < data.length; i++) {
141: if (data[i] == BLANK) {
142: data[i] = UNDERSCORE;
143: }
144: }
145: }
146: return data;
147: }
148:
149: protected byte[] doDecoding(byte[] bytes) throws DecoderException {
150: if (bytes == null) {
151: return null;
152: }
153: boolean hasUnderscores = false;
154: for (int i = 0; i < bytes.length; i++) {
155: if (bytes[i] == UNDERSCORE) {
156: hasUnderscores = true;
157: break;
158: }
159: }
160: if (hasUnderscores) {
161: byte[] tmp = new byte[bytes.length];
162: for (int i = 0; i < bytes.length; i++) {
163: byte b = bytes[i];
164: if (b != UNDERSCORE) {
165: tmp[i] = b;
166: } else {
167: tmp[i] = BLANK;
168: }
169: }
170: return QuotedPrintableCodec.decodeQuotedPrintable(tmp);
171: }
172: return QuotedPrintableCodec.decodeQuotedPrintable(bytes);
173: }
174:
175: /**
176: * Encodes a string into its quoted-printable form using the specified charset. Unsafe characters are escaped.
177: *
178: * @param pString
179: * string to convert to quoted-printable form
180: * @param charset
181: * the charset for pString
182: * @return quoted-printable string
183: *
184: * @throws EncoderException
185: * thrown if a failure condition is encountered during the encoding process.
186: */
187: public String encode(final String pString, final String charset)
188: throws EncoderException {
189: if (pString == null) {
190: return null;
191: }
192: try {
193: return encodeText(pString, charset);
194: } catch (UnsupportedEncodingException e) {
195: throw new EncoderException(e.getMessage());
196: }
197: }
198:
199: /**
200: * Encodes a string into its quoted-printable form using the default charset. Unsafe characters are escaped.
201: *
202: * @param pString
203: * string to convert to quoted-printable form
204: * @return quoted-printable string
205: *
206: * @throws EncoderException
207: * thrown if a failure condition is encountered during the encoding process.
208: */
209: public String encode(String pString) throws EncoderException {
210: if (pString == null) {
211: return null;
212: }
213: return encode(pString, getDefaultCharset());
214: }
215:
216: /**
217: * Decodes a quoted-printable string into its original form. Escaped characters are converted back to their original
218: * representation.
219: *
220: * @param pString
221: * quoted-printable string to convert into its original form
222: *
223: * @return original string
224: *
225: * @throws DecoderException
226: * A decoder exception is thrown if a failure condition is encountered during the decode process.
227: */
228: public String decode(String pString) throws DecoderException {
229: if (pString == null) {
230: return null;
231: }
232: try {
233: return decodeText(pString);
234: } catch (UnsupportedEncodingException e) {
235: throw new DecoderException(e.getMessage());
236: }
237: }
238:
239: /**
240: * Encodes an object into its quoted-printable form using the default charset. Unsafe characters are escaped.
241: *
242: * @param pObject
243: * object to convert to quoted-printable form
244: * @return quoted-printable object
245: *
246: * @throws EncoderException
247: * thrown if a failure condition is encountered during the encoding process.
248: */
249: public Object encode(Object pObject) throws EncoderException {
250: if (pObject == null) {
251: return null;
252: } else if (pObject instanceof String) {
253: return encode((String) pObject);
254: } else {
255: throw new EncoderException("Objects of type "
256: + pObject.getClass().getName()
257: + " cannot be encoded using Q codec");
258: }
259: }
260:
261: /**
262: * Decodes a quoted-printable object into its original form. Escaped characters are converted back to their original
263: * representation.
264: *
265: * @param pObject
266: * quoted-printable object to convert into its original form
267: *
268: * @return original object
269: *
270: * @throws DecoderException
271: * A decoder exception is thrown if a failure condition is encountered during the decode process.
272: */
273: public Object decode(Object pObject) throws DecoderException {
274: if (pObject == null) {
275: return null;
276: } else if (pObject instanceof String) {
277: return decode((String) pObject);
278: } else {
279: throw new DecoderException("Objects of type "
280: + pObject.getClass().getName()
281: + " cannot be decoded using Q codec");
282: }
283: }
284:
285: /**
286: * The default charset used for string decoding and encoding.
287: *
288: * @return the default string charset.
289: */
290: public String getDefaultCharset() {
291: return this .charset;
292: }
293:
294: /**
295: * Tests if optional tranformation of SPACE characters is to be used
296: *
297: * @return <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
298: */
299: public boolean isEncodeBlanks() {
300: return this .encodeBlanks;
301: }
302:
303: /**
304: * Defines whether optional tranformation of SPACE characters is to be used
305: *
306: * @param b
307: * <code>true</code> if SPACE characters are to be transformed, <code>false</code> otherwise
308: */
309: public void setEncodeBlanks(boolean b) {
310: this.encodeBlanks = b;
311: }
312: }
|