KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > columba > ristretto > coder > QuotedPrintable


1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
4  * The contents of this file are subject to the Mozilla Public License Version
5  * 1.1 (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  * http://www.mozilla.org/MPL/
8  *
9  * Software distributed under the License is distributed on an "AS IS" basis,
10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11  * for the specific language governing rights and limitations under the
12  * License.
13  *
14  * The Original Code is Ristretto Mail API.
15  *
16  * The Initial Developers of the Original Code are
17  * Timo Stich and Frederik Dietz.
18  * Portions created by the Initial Developers are Copyright (C) 2004
19  * All Rights Reserved.
20  *
21  * Contributor(s):
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */

36 package org.columba.ristretto.coder;
37
38 import java.io.UnsupportedEncodingException JavaDoc;
39 import java.nio.ByteBuffer JavaDoc;
40 import java.nio.CharBuffer JavaDoc;
41 import java.nio.charset.Charset JavaDoc;
42 import java.util.logging.Logger JavaDoc;
43 import java.util.regex.Matcher JavaDoc;
44 import java.util.regex.Pattern JavaDoc;
45
46 /**
47  * Implementation of QuotedPrintable en- and decoding methods.
48  * QuotedPrintable encoding is used to transform a text that contains
49  * non-US-ASCII characters into a US-ASCII compatible text while
50  * maintaining human readability as far as possible.
51  * <br>
52  * <b>RFC(s):</b> 2045
53  */

54 public class QuotedPrintable {
55
56     /** JDK 1.4+ logging framework logger, used for logging. */
57     private static final Logger JavaDoc LOG = Logger.getLogger("org.columba.ristretto.coder");
58
59
60     private static final char[] hexTable =
61         {
62             '0',
63             '1',
64             '2',
65             '3',
66             '4',
67             '5',
68             '6',
69             '7',
70             '8',
71             '9',
72             'A',
73             'B',
74             'C',
75             'D',
76             'E',
77             'F' };
78
79     private static final Pattern JavaDoc packPattern =
80         Pattern.compile("=([0-9a-fA-F\r][0-9a-fA-F\n])");
81
82     /**
83      * Decodes a QuotedPrintable encoded text
84      * to its original form using the given Charset.
85      *
86     * @param input The QuotedPrintable encoded text in form of a {@link CharSequence}
87      * @param charset The {@link Charset} of the decoded text
88      * @return {@link StringBuffer} using the given charset
89      */

90     public static CharSequence JavaDoc decode(CharSequence JavaDoc input, Charset JavaDoc charset) {
91         ByteBuffer JavaDoc buffer = ByteBuffer.allocate(input.length());
92         Matcher JavaDoc matcher = packPattern.matcher(input);
93         int lastMatchEnd = 0;
94         String JavaDoc group;
95
96         // Process found packs
97

98         while (matcher.find()) {
99             try {
100                 // Add the bytes from the part that isn't encoded to the buffer
101
buffer.put(input.subSequence(lastMatchEnd, matcher.start()).toString().getBytes("US-ASCII"));
102             } catch (UnsupportedEncodingException JavaDoc e) {
103                 // Never happens, because US-ASCII is always shipped
104
LOG.severe(e.getMessage());
105             }
106             group = matcher.group(1);
107             if (!group.equals("\r\n")) {
108                 buffer.put((byte) Integer.parseInt(group, 16));
109             }
110             lastMatchEnd = matcher.end();
111         }
112
113         // Append the rest of the input string
114
try {
115             // Add the bytes from the part that isn't encoded to the buffer
116
buffer.put(input.subSequence(lastMatchEnd, input.length()).toString().getBytes("US-ASCII"));
117         } catch (UnsupportedEncodingException JavaDoc e) {
118             // Never happens, because US-ASCII is always shipped
119
LOG.severe(e.getMessage());
120         }
121         buffer.limit(buffer.position());
122         buffer.rewind();
123         
124         return charset.decode(buffer);
125     }
126
127     /**
128  * Encodes a CharSequence with QuotedPrintable Coding.
129  * <br>
130  * <b>Note:</b> The input is transformed to canonical form before encoding
131  *
132  * @param input The input text in form of a {@link CharSequence}
133  * @param charset The charactercoding that the input uses (e.g. ISO-8859-1)
134  * @return a US-ASCII compatible {@link StringBuffer}
135  */

136 public static StringBuffer JavaDoc encode(CharSequence JavaDoc input, Charset JavaDoc charset) {
137         StringBuffer JavaDoc result = new StringBuffer JavaDoc(input.length());
138         CharBuffer JavaDoc current = CharBuffer.allocate(1);
139         ByteBuffer JavaDoc decoded;
140         int lineLength = 0;
141
142         for (int i = 0; i < input.length(); i++) {
143             current.rewind();
144             current.put(0, input.charAt(i));
145             decoded = charset.encode(current);
146
147             // chars must be encoded when not :
148
// 33 <= c <=60; 62 <= c <= 126 (literal characters)
149
// c = {9,32} but not at the end of a line or if they are char at linePos 74
150
// because a soft linebreak will follow (whitespaces)
151
// c = {\r,\n} (linebreak)
152
while( decoded.remaining() != 0) {
153             byte next = decoded.get();
154             
155             if (((next == '\t' || next == ' ')
156                 && !(lineLength == 74 || input.charAt(i + 1) == '\r' || input.charAt(i + 1) == '\n'))
157                 || (next >= 33
158                     && next != 61
159                     && next <= 126)) {
160                 result.append((char) next);
161                 lineLength++;
162             } else if (next == '\r') {
163                 result.append("\r\n");
164                 i++;
165                 lineLength = 0;
166             } else {
167                 if (lineLength > 71) {
168                     result.append("=\r\n");
169                     lineLength = 0;
170                 }
171                 result.append('=');
172                 result.append(toHexString(next));
173                 lineLength += 3;
174             }
175
176             if (lineLength > 74) {
177                 result.append("=\r\n");
178                 lineLength = 0;
179             }
180         }
181         }
182
183         return result;
184     }
185
186     /**
187      * Converts a byte-value into a hex number
188      *
189      * @param in
190      * @return the hex number
191      */

192     private static char[] toHexString(byte in) {
193         char[] result = new char[2];
194         int value;
195         if (in < 0) {
196             value = 0x080 | (0x07f & in);
197         } else {
198             value = in;
199         }
200
201         int hi = value / 16;
202         int lo = value % 16;
203
204         result[0] = hexTable[hi];
205         result[1] = hexTable[lo];
206
207         return result;
208     }
209
210 }
211
Popular Tags