View Javadoc

1   /*
2   Copyright (C) 2000 - 2007 Grid Systems, S.A.
3   
4   This program is free software; you can redistribute it and/or modify
5   it under the terms of the GNU General Public License, version 2, as
6   published by the Free Software Foundation.
7   
8   This program is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  GNU General Public License for more details.
12  
13  You should have received a copy of the GNU General Public License
14  along with this program; if not, write to the Free Software
15  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16  */
17  
18  package com.gridsystems;
19  
20  import java.io.ByteArrayOutputStream;
21  import java.io.FilterOutputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  
26  /**
27   * A class to encode Base64 streams and strings.
28   * See RFC 1521 section 5.2 for details of the Base64 algorithm.
29   * <p>
30   * This class can be used for encoding strings:
31   * <blockquote><pre>
32   * String unencoded = "webmaster:try2gueSS";
33   * String encoded = Base64Encoder.encode(unencoded);
34   * </pre></blockquote>
35   * or for encoding streams:
36   * <blockquote><pre>
37   * OutputStream out = new Base64Encoder(System.out);
38   * </pre></blockquote>
39   *
40   * @author <b>Jason Hunter</b>, Copyright &#169; 2000
41   * @author Rodrigo Ruiz (modifications for internal use by GridSystems)
42   * @version 1.1, 2000/11/17, fixed bug with sign bit for char values
43   * @version 1.0, 2000/06/11
44   */
45  public class Base64Encoder extends FilterOutputStream {
46  
47    /** Mapping from six-bit integers to char values. */
48    private static final char[] CHARS = {
49      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
50      'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
51      'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
52      'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
53      'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
54      'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
55      '8', '9', '+', '/'
56    };
57  
58    /** Char count. */
59    private int charCount;
60    /** Carryover. */
61    private int carryOver;
62  
63    /**
64     * Constructs a new Base64 encoder that writes output to the given
65     * OutputStream.
66     *
67     * @param out the output stream
68     */
69    public Base64Encoder(OutputStream out) {
70      super(out);
71    }
72  
73    /**
74     * Writes the given byte to the output stream in an encoded form.
75     *
76     * @param b the byte to write
77     * @throws IOException if an I/O error occurs
78     */
79    public void write(int b) throws IOException {
80      // Take 24-bits from three octets, translate into four encoded chars
81      // Break lines at 76 chars
82      // If necessary, pad with 0 bits on the right at the end
83      // Use = signs as padding at the end to ensure encodedLength % 4 == 0
84  
85      // Remove the sign bit,
86      // thanks to Christian Schweingruber <chrigu@lorraine.ch>
87      if (b < 0) {
88        b += 256;
89      }
90  
91      // First byte use first six bits, save last two bits
92      if (charCount % 3 == 0) {
93        int lookup = b >> 2;
94        carryOver = b & 3;        // last two bits
95        out.write(CHARS[lookup]);
96      } else if (charCount % 3 == 1) {
97        // Second byte use previous two bits and first four new bits,
98        // save last four bits
99        int lookup = ((carryOver << 4) + (b >> 4)) & 63;
100       carryOver = b & 15;       // last four bits
101       out.write(CHARS[lookup]);
102     } else if (charCount % 3 == 2) {
103       // Third byte use previous four bits and first two new bits,
104       // then use last six new bits
105       int lookup = ((carryOver << 2) + (b >> 6)) & 63;
106       out.write(CHARS[lookup]);
107       lookup = b & 63;          // last six bits
108       out.write(CHARS[lookup]);
109       carryOver = 0;
110     }
111     charCount++;
112 
113     // Add newline every 76 output chars (that's 57 input chars)
114     if (charCount % 57 == 0) {
115       out.write('\n');
116     }
117   }
118 
119   /**
120    * Writes the given byte array to the output stream in an
121    * encoded form.
122    *
123    * @param b the data to be written
124    * @param off the start offset of the data
125    * @param len the length of the data
126    * @exception IOException if an I/O error occurs
127    */
128   public void write(byte[] b, int off, int len) throws IOException {
129     // This could of course be optimized
130     for (int i = 0; i < len; i++) {
131       write(b[off + i]);
132     }
133   }
134 
135   /**
136    * Write a String to the output stream.
137    * @param s the string to write
138    * @throws IOException if an I/O error occurs
139    */
140   public void write(String s) throws IOException {
141     byte[] buf = Base64Utils.toByteArray(s);
142     write(buf, 0, buf.length);
143   }
144 
145   /**
146    * Closes the stream, this MUST be called to ensure proper padding is
147    * written to the end of the output stream.
148    *
149    * @exception IOException if an I/O error occurs
150    */
151   public void close() throws IOException {
152     // Handle leftover bytes
153     if (charCount % 3 == 1) {  // one leftover
154       int lookup = (carryOver << 4) & 63;
155       out.write(CHARS[lookup]);
156       out.write('=');
157       out.write('=');
158     } else if (charCount % 3 == 2) {  // two leftovers
159       int lookup = (carryOver << 2) & 63;
160       out.write(CHARS[lookup]);
161       out.write('=');
162     }
163     super.close();
164   }
165 
166   /**
167    * Returns the Base64 encoded form of the given string.
168    *
169    * @param s the string to encode
170    * @return  the encoded form of the unencoded string
171    */
172   public static String encode(String s) {
173     ByteArrayOutputStream out = new ByteArrayOutputStream((int) (s.length() * 1.37));
174     Base64Encoder encodedOut = new Base64Encoder(out);
175 
176     try {
177       encodedOut.write(s);
178       encodedOut.close();
179 
180       // No need to specify a character encoding. Base64 uses a subset of ASCII
181       return out.toString();
182     } catch (IOException ignored) { return null; }
183   }
184 
185   /**
186    * Returns the encoded form of the given unencoded byte array.
187    *
188    * @param unencoded the byte array to encode
189    * @return the encoded form of the unencoded byte array
190    */
191   public static byte[] binaryEncode(byte[] unencoded) {
192     ByteArrayOutputStream out = new ByteArrayOutputStream((int)(unencoded.length * 1.37));
193     Base64Encoder encodedOut = new Base64Encoder(out);
194 
195     try {
196       encodedOut.write(unencoded);
197       encodedOut.close();
198 
199       return out.toByteArray();
200     } catch (IOException ignored) { return null; }
201   }
202 
203   /**
204    * Returns the encoded form of the given unencoded byte array.
205    *
206    * @param is the input stream to encode
207    * @return the encoded form of the unencoded byte array
208    */
209   public static byte[] binaryEncode(InputStream is) {
210     ByteArrayOutputStream out = new ByteArrayOutputStream();
211     Base64Encoder encodedOut = new Base64Encoder(out);
212 
213     try {
214       byte[] data = new byte[128000];
215 
216       int size;
217 
218       while ((size = is.read(data)) != -1) {
219         encodedOut.write(data, 0, size);
220       }
221       encodedOut.close();
222 
223       byte[] retorno =  out.toByteArray();
224       out = null;
225 
226       return retorno;
227     } catch (IOException ignored) { return null; }
228   }
229 }