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