1 /*
2 * Copyright 2005 Ralf Joachim
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.castor.core.util;
17
18 /**
19 * Class encodes the bytes written to the OutPutStream to a Base64 encoded string.
20 * The encoded string can be retrieved by as a whole by the toString() method or
21 * splited into lines of 72 characters by the toStringArray() method.
22 *
23 * @author <a href="mailto:ralf DOT joachim AT syscon DOT eu">Ralf Joachim</a>
24 * @version $Revision: 6907 $ $Date: 2005-08-05 13:58:36 -0600 (Fri, 05 Aug 2005) $
25 * @since 0.9.9
26 */
27 public final class Base64Encoder {
28 /** Third octets in buffer. */
29 private static final int OCTET_3 = 3;
30
31 /** Mask buffer to or with first octet. */
32 private static final int OCTET_1_MASK = 0x00FFFF;
33
34 /** Mask buffer to or with second octet. */
35 private static final int OCTET_2_MASK = 0xFF00FF;
36
37 /** Mask buffer to or with third octet. */
38 private static final int OCTET_3_MASK = 0xFFFF00;
39
40 /** Mask an octet. */
41 private static final int OCTET_MASK = 0xFF;
42
43 /** Number of bits to shift for one octet. */
44 private static final int SHIFT_1_OCTET = 8;
45
46 /** Number of bits to shift for two octet. */
47 private static final int SHIFT_2_OCTET = 16;
48
49 /** Mask a sextet. */
50 private static final int SEXTET_MASK = 0x3F;
51
52 /** Number of bits to shift for one sextet. */
53 private static final int SHIFT_1_SEXTET = 6;
54
55 /** Number of bits to shift for two sextet. */
56 private static final int SHIFT_2_SEXTET = 12;
57
58 /** Number of bits to shift for three sextet. */
59 private static final int SHIFT_3_SEXTET = 18;
60
61 /** Array to convert sextet byte values into base64 characters. */
62 private static final char[] MAP = {
63 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 00-07
64 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 08-15
65 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16-23
66 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24-31
67 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32-39
68 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40-47
69 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48-55
70 '4', '5', '6', '7', '8', '9', '+', '/', // 56-63
71 };
72
73 /** 24-bit buffer to translate 3 octets into 4 sextets. */
74 private int _buffer = 0;
75
76 /** Number of octets in buffer. */
77 private int _octets = 0;
78
79 /** Stream buffer for encoded characters waiting to be read. */
80 private StringBuffer _stream = new StringBuffer();
81
82 /**
83 * Encode given byte array into a encoded character array.
84 *
85 * @param bytes The byte array to be encoded.
86 * @return Base64 encoded characters as an array.
87 */
88 public static char[] encode(final byte[] bytes) {
89 Base64Encoder enc = new Base64Encoder();
90 enc.translate(bytes);
91 return enc.getCharArray();
92 }
93
94 /**
95 * Construct a Base64Encoder.
96 */
97 public Base64Encoder() { }
98
99 /**
100 * Reset Base64Encoder to its initial state. Take care using this method as it
101 * throws all previously written bytes away.
102 */
103 public void reset() {
104 _buffer = 0;
105 _octets = 0;
106 _stream = new StringBuffer();
107 }
108
109 /**
110 * Translate all bytes of given array by appending each to octet buffer. If
111 * buffer contains 3 octets its content will be encoded to 4 sextet byte values
112 * which are converted to a base64 character each. All characters are appended
113 * to a StringBuffer.
114 *
115 * @param bytes The byte array to be encoded.
116 */
117 public void translate(final byte[] bytes) {
118 for (int i = 0; i < bytes.length; i++) {
119 byte b = bytes[i];
120
121 if (_octets == 0) {
122 _buffer = (_buffer & OCTET_1_MASK) | ((b & OCTET_MASK) << SHIFT_2_OCTET);
123 } else if (_octets == 1) {
124 _buffer = (_buffer & OCTET_2_MASK) | ((b & OCTET_MASK) << SHIFT_1_OCTET);
125 } else {
126 _buffer = (_buffer & OCTET_3_MASK) | (b & OCTET_MASK);
127 }
128
129 if ((++_octets) == OCTET_3) { encode(); }
130 }
131 }
132
133 /**
134 * Encode 4 sextets from buffer and add them to StringBuffer.
135 */
136 private void encode() {
137 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_3_SEXTET)]); // sextet 1
138 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_2_SEXTET)]); // sextet 2
139 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_1_SEXTET)]); // sextet 3
140 _stream.append(MAP[SEXTET_MASK & _buffer]); // sextet 4
141 _buffer = 0;
142 _octets = 0;
143 }
144
145 /**
146 * Encode the remaining sextets from buffer and add them to to StringBuffer.
147 */
148 private void encodeWithPadding() {
149 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_3_SEXTET)]); // sextet 1
150 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_2_SEXTET)]); // sextet 2
151 if (_octets <= 1) { // sextet 3
152 _stream.append('=');
153 } else {
154 _stream.append(MAP[SEXTET_MASK & (_buffer >> SHIFT_1_SEXTET)]);
155 }
156 if (_octets <= 2) { // sextet 4
157 _stream.append('=');
158 } else {
159 _stream.append(MAP[SEXTET_MASK & _buffer]);
160 }
161 _buffer = 0;
162 _octets = 0;
163 }
164
165 /**
166 * Get Base64 encoded characters as an array.
167 *
168 * @return Base64 encoded characters as an array.
169 */
170 public char[] getCharArray() {
171 if (_octets > 0) { encodeWithPadding(); }
172 char[] chars = new char[_stream.length()];
173 if (_stream.length() > 0) { _stream.getChars(0, _stream.length(), chars, 0); }
174 return chars;
175 }
176 }