Base64.java

  1. /*
  2.  *
  3.  * The DbUnit Database Testing Framework
  4.  * Copyright (C)2002-2004, DbUnit.org
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2.1 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  *
  20.  */
  21. package org.dbunit.util;

  22. import java.io.FileInputStream;

  23. import org.slf4j.Logger;
  24. import org.slf4j.LoggerFactory;

  25. /**
  26.  * <p>
  27.  * I am placing this code in the Public Domain. Do with it as you will.
  28.  * This software comes with no guarantees or warranties but with
  29.  * plenty of well-wishing instead!
  30.  * Please visit <a href="http://iharder.net/base64">http://iharder.net/base64</a>
  31.  * periodically to check for updates or to contribute improvements.
  32.  * </p>
  33.  *
  34.  * @author Robert Harder (rharder@usa.net)
  35.  * @author Last changed by: $Author$
  36.  * @version $Revision$ $Date$
  37.  * @since 1.3
  38.  */
  39. public class Base64
  40. {

  41.     /**
  42.      * Logger for this class
  43.      */
  44.     private static final Logger logger = LoggerFactory.getLogger(Base64.class);

  45.     /** Specify encoding (value is <tt>true</tt>). */
  46.     public final static boolean ENCODE = true;


  47.     /** Specify decoding (value is <tt>false</tt>). */
  48.     public final static boolean DECODE = false;


  49.     /** Maximum line length (76) of Base64 output. */
  50.     private final static int MAX_LINE_LENGTH = 76;


  51.     /** The equals sign (=) as a byte. */
  52.     private final static byte EQUALS_SIGN = (byte)'=';


  53.     /** The new line character (\n) as a byte. */
  54.     private final static byte NEW_LINE = (byte)'\n';


  55.     /** The 64 valid Base64 values. */
  56.     private final static byte[] ALPHABET =
  57.             {
  58.                 (byte)'A', (byte)'B', (byte)'C', (byte)'D', (byte)'E', (byte)'F', (byte)'G',
  59.                 (byte)'H', (byte)'I', (byte)'J', (byte)'K', (byte)'L', (byte)'M', (byte)'N',
  60.                 (byte)'O', (byte)'P', (byte)'Q', (byte)'R', (byte)'S', (byte)'T', (byte)'U',
  61.                 (byte)'V', (byte)'W', (byte)'X', (byte)'Y', (byte)'Z',
  62.                 (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f', (byte)'g',
  63.                 (byte)'h', (byte)'i', (byte)'j', (byte)'k', (byte)'l', (byte)'m', (byte)'n',
  64.                 (byte)'o', (byte)'p', (byte)'q', (byte)'r', (byte)'s', (byte)'t', (byte)'u',
  65.                 (byte)'v', (byte)'w', (byte)'x', (byte)'y', (byte)'z',
  66.                 (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5',
  67.                 (byte)'6', (byte)'7', (byte)'8', (byte)'9', (byte)'+', (byte)'/'
  68.             };

  69.     /**
  70.      * Translates a Base64 value to either its 6-bit reconstruction value
  71.      * or a negative number indicating some other meaning.
  72.      **/
  73.     private final static byte[] DECODABET =
  74.             {
  75.                 -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal  0 -  8
  76.                 -5, -5, // Whitespace: Tab and Linefeed
  77.                 -9, -9, // Decimal 11 - 12
  78.                 -5, // Whitespace: Carriage Return
  79.                 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26
  80.                 -9, -9, -9, -9, -9, // Decimal 27 - 31
  81.                 -5, // Whitespace: Space
  82.                 -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42
  83.                 62, // Plus sign at decimal 43
  84.                 -9, -9, -9, // Decimal 44 - 46
  85.                 63, // Slash at decimal 47
  86.                 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine
  87.                 -9, -9, -9, // Decimal 58 - 60
  88.                 -1, // Equals sign at decimal 61
  89.                 -9, -9, -9, // Decimal 62 - 64
  90.                 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N'
  91.                 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z'
  92.                 -9, -9, -9, -9, -9, -9, // Decimal 91 - 96
  93.                 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm'
  94.                 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z'
  95.                 -9, -9, -9, -9                                 // Decimal 123 - 126
  96.                 /*,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 127 - 139
  97.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 140 - 152
  98.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 153 - 165
  99.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 166 - 178
  100.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 179 - 191
  101.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 192 - 204
  102.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 205 - 217
  103.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 218 - 230
  104.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,     // Decimal 231 - 243
  105.                 -9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9,-9         // Decimal 244 - 255 */
  106.             };

  107.     private final static byte BAD_ENCODING = -9; // Indicates error in encoding
  108.     private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding
  109.     private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding


  110.     /** Defeats instantiation. */
  111.     private Base64()
  112.     {
  113.     }


  114.     /** Testing. */
  115.     public static void main(String[] args)
  116.     {
  117.         logger.debug("main(args=" + args + ") - start");

  118.         String s = "Hello, world";
  119.         s = "abcd";
  120.         //s = System.getProperties().toString();
  121.         //System.out.println( s + ": \n [" + encode( s ) + "]\n [" + decode(encode(s)) + "]" );

  122.         byte[] b = encodeString(s).getBytes();
  123.         byte[] c = decode(b, 0, b.length);

  124.         System.out.println("\n\n" + s + ":" + new String(b) + ":" + new String(c));

  125.         try
  126.         {
  127.             FileInputStream fis = new FileInputStream("c:\\abcd.txt");
  128.             InputStream b64is = new InputStream(fis, DECODE);
  129.             int ib = 0;
  130.             while ((ib = b64is.read()) > 0)
  131.             {   //System.out.print( new String( ""+(char)ib ) );
  132.             }
  133.         }   // end try
  134.         catch (Exception e)
  135.         {
  136.             logger.error("main()", e);

  137.             e.printStackTrace();
  138.         }
  139.     }


  140. /* ********  E N C O D I N G   M E T H O D S  ******** */


  141.     /**
  142.      * Encodes the first three bytes of array <var>threeBytes</var>
  143.      * and returns a four-byte array in Base64 notation.
  144.      *
  145.      * @param threeBytes the array to convert
  146.      * @return four byte array in Base64 notation.
  147.      * @since 1.3
  148.      */
  149.     private static byte[] encode3to4(byte[] threeBytes)
  150.     {
  151.         logger.debug("encode3to4(threeBytes=" + threeBytes + ") - start");

  152.         return encode3to4(threeBytes, 3);
  153.     }   // end encodeToBytes


  154.     /**
  155.      * Encodes up to the first three bytes of array <var>threeBytes</var>
  156.      * and returns a four-byte array in Base64 notation.
  157.      * The actual number of significant bytes in your array is
  158.      * given by <var>numSigBytes</var>.
  159.      * The array <var>threeBytes</var> needs only be as big as
  160.      * <var>numSigBytes</var>.
  161.      *
  162.      * @param threeBytes the array to convert
  163.      * @param numSigBytes the number of significant bytes in your array
  164.      * @return four byte array in Base64 notation.
  165.      * @since 1.3
  166.      */
  167.     private static byte[] encode3to4(byte[] threeBytes, int numSigBytes)
  168.     {
  169.         logger.debug("encode3to4(threeBytes=" + threeBytes + ", numSigBytes=" + numSigBytes + ") - start");

  170.         byte[] dest = new byte[4];
  171.         encode3to4(threeBytes, 0, numSigBytes, dest, 0);
  172.         return dest;
  173.     }


  174.     /**
  175.      * Encodes up to three bytes of the array <var>source</var>
  176.      * and writes the resulting four Base64 bytes to <var>destination</var>.
  177.      * The source and destination arrays can be manipulated
  178.      * anywhere along their length by specifying
  179.      * <var>srcOffset</var> and <var>destOffset</var>.
  180.      * This method does not check to make sure your arrays
  181.      * are large enough to accomodate <var>srcOffset</var> + 3 for
  182.      * the <var>source</var> array or <var>destOffset</var> + 4 for
  183.      * the <var>destination</var> array.
  184.      * The actual number of significant bytes in your array is
  185.      * given by <var>numSigBytes</var>.
  186.      *
  187.      * @param source the array to convert
  188.      * @param srcOffset the index where conversion begins
  189.      * @param numSigBytes the number of significant bytes in your array
  190.      * @param destination the array to hold the conversion
  191.      * @param destOffset the index where output will be put
  192.      * @return the <var>destination</var> array
  193.      * @since 1.3
  194.      */
  195.     private static byte[] encode3to4(
  196.             byte[] source, int srcOffset, int numSigBytes,
  197.             byte[] destination, int destOffset)
  198.     {
  199.         if(logger.isDebugEnabled())
  200.             logger.debug("encode3to4(source=" + source + ", srcOffset=" + srcOffset + ", numSigBytes=" + numSigBytes
  201.                 + ", destination=" + destination + ", destOffset=" + destOffset + ") - start");

  202.         //           1         2         3
  203.         // 01234567890123456789012345678901 Bit position
  204.         // --------000000001111111122222222 Array position from threeBytes
  205.         // --------|    ||    ||    ||    | Six bit groups to index ALPHABET
  206.         //          >>18  >>12  >> 6  >> 0  Right shift necessary
  207.         //                0x3f  0x3f  0x3f  Additional AND

  208.         // Create buffer with zero-padding if there are only one or two
  209.         // significant bytes passed in the array.
  210.         // We have to shift left 24 in order to flush out the 1's that appear
  211.         // when Java treats a value as negative that is cast from a byte to an int.
  212.         int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0)
  213.                 | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0)
  214.                 | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0);

  215.         switch (numSigBytes)
  216.         {
  217.             case 3:
  218.                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  219.                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  220.                 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
  221.                 destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f];
  222.                 return destination;

  223.             case 2:
  224.                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  225.                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  226.                 destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f];
  227.                 destination[destOffset + 3] = EQUALS_SIGN;
  228.                 return destination;

  229.             case 1:
  230.                 destination[destOffset] = ALPHABET[(inBuff >>> 18)];
  231.                 destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f];
  232.                 destination[destOffset + 2] = EQUALS_SIGN;
  233.                 destination[destOffset + 3] = EQUALS_SIGN;
  234.                 return destination;

  235.             default:
  236.                 return destination;
  237.         }   // end switch
  238.     }   // end encode3to4


  239.     /**
  240.      * Serializes an object and returns the Base64-encoded
  241.      * version of that serialized object. If the object
  242.      * cannot be serialized or there is another error,
  243.      * the method will return <tt>null</tt>.
  244.      *
  245.      * @param serializableObject The object to encode
  246.      * @return The Base64-encoded object
  247.      * @since 1.4
  248.      */
  249.     public static String encodeObject(java.io.Serializable serializableObject)
  250.     {
  251.         if(logger.isDebugEnabled())
  252.             logger.debug("encodeObject(serializableObject=" + serializableObject + ") - start");

  253.         java.io.ByteArrayOutputStream baos = null;
  254.         java.io.OutputStream b64os = null;
  255.         java.io.ObjectOutputStream oos = null;

  256.         try
  257.         {
  258.             baos = new java.io.ByteArrayOutputStream();
  259.             b64os = new OutputStream(baos, Base64.ENCODE);
  260.             oos = new java.io.ObjectOutputStream(b64os);

  261.             oos.writeObject(serializableObject);
  262.         }   // end try
  263.         catch (java.io.IOException e)
  264.         {
  265.             logger.error("encodeObject()", e);

  266.             e.printStackTrace();
  267.             return null;
  268.         }   // end catch
  269.         finally
  270.         {
  271.             try
  272.             {
  273.                 oos.close();
  274.             }
  275.             catch (Exception e)
  276.             {
  277.                 logger.error("encodeObject()", e);
  278.             }
  279.             try
  280.             {
  281.                 b64os.close();
  282.             }
  283.             catch (Exception e)
  284.             {
  285.                 logger.error("encodeObject()", e);
  286.             }
  287.             try
  288.             {
  289.                 baos.close();
  290.             }
  291.             catch (Exception e)
  292.             {
  293.                 logger.error("encodeObject()", e);
  294.             }
  295.         }   // end finally

  296.         return new String(baos.toByteArray());
  297.     }   // end encode


  298.     /**
  299.      * Encodes a byte array into Base64 notation.
  300.      * Equivalen to calling
  301.      * <code>encodeBytes( source, 0, source.length )</code>
  302.      *
  303.      * @param source The data to convert
  304.      * @since 1.4
  305.      */
  306.     public static String encodeBytes(byte[] source)
  307.     {
  308.         if(logger.isDebugEnabled())
  309.             logger.debug("encodeBytes(source=" + source + ") - start");

  310.         return encodeBytes(source, 0, source.length);
  311.     }   // end encodeBytes


  312.     /**
  313.      * Encodes a byte array into Base64 notation.
  314.      *
  315.      * @param source The data to convert
  316.      * @param off Offset in array where conversion should begin
  317.      * @param len Length of data to convert
  318.      * @since 1.4
  319.      */
  320.     public static String encodeBytes(byte[] source, int off, int len)
  321.     {
  322.         if(logger.isDebugEnabled())
  323.             logger.debug("encodeBytes(source=" + source + ", off=" + off + ", len=" + len + ") - start");

  324.         int len43 = len * 4 / 3;
  325.         byte[] outBuff = new byte[(len43)                      // Main 4:3
  326.                 + ((len % 3) > 0 ? 4 : 0)      // Account for padding
  327.                 + (len43 / MAX_LINE_LENGTH)]; // New lines
  328.         int d = 0;
  329.         int e = 0;
  330.         int len2 = len - 2;
  331.         int lineLength = 0;
  332.         for (; d < len2; d += 3, e += 4)
  333.         {
  334.             encode3to4(source, d, 3, outBuff, e);

  335.             lineLength += 4;
  336.             if (lineLength == MAX_LINE_LENGTH)
  337.             {
  338.                 outBuff[e + 4] = NEW_LINE;
  339.                 e++;
  340.                 lineLength = 0;
  341.             }   // end if: end of line
  342.         }   // en dfor: each piece of array

  343.         if (d < len)
  344.         {
  345.             encode3to4(source, d, len - d, outBuff, e);
  346.             e += 4;
  347.         }   // end if: some padding needed

  348.         return new String(outBuff, 0, e);
  349.     }   // end encodeBytes


  350.     /**
  351.      * Encodes a string in Base64 notation with line breaks
  352.      * after every 75 Base64 characters.
  353.      *
  354.      * @param s the string to encode
  355.      * @return the encoded string
  356.      * @since 1.3
  357.      */
  358.     public static String encodeString(String s)
  359.     {
  360.         logger.debug("encodeString(s={}) - start", s);

  361.         return encodeBytes(s.getBytes());
  362.     }   // end encodeString




  363. /* ********  D E C O D I N G   M E T H O D S  ******** */


  364.     /**
  365.      * Decodes the first four bytes of array <var>fourBytes</var>
  366.      * and returns an array up to three bytes long with the
  367.      * decoded values.
  368.      *
  369.      * @param fourBytes the array with Base64 content
  370.      * @return array with decoded values
  371.      * @since 1.3
  372.      */
  373.     private static byte[] decode4to3(byte[] fourBytes)
  374.     {
  375.         if(logger.isDebugEnabled())
  376.             logger.debug("decode4to3(fourBytes=" + fourBytes + ") - start");

  377.         byte[] outBuff1 = new byte[3];
  378.         int count = decode4to3(fourBytes, 0, outBuff1, 0);
  379.         byte[] outBuff2 = new byte[count];

  380.         for (int i = 0; i < count; i++)
  381.             outBuff2[i] = outBuff1[i];

  382.         return outBuff2;
  383.     }


  384.     /**
  385.      * Decodes four bytes from array <var>source</var>
  386.      * and writes the resulting bytes (up to three of them)
  387.      * to <var>destination</var>.
  388.      * The source and destination arrays can be manipulated
  389.      * anywhere along their length by specifying
  390.      * <var>srcOffset</var> and <var>destOffset</var>.
  391.      * This method does not check to make sure your arrays
  392.      * are large enough to accomodate <var>srcOffset</var> + 4 for
  393.      * the <var>source</var> array or <var>destOffset</var> + 3 for
  394.      * the <var>destination</var> array.
  395.      * This method returns the actual number of bytes that
  396.      * were converted from the Base64 encoding.
  397.      *
  398.      *
  399.      * @param source the array to convert
  400.      * @param srcOffset the index where conversion begins
  401.      * @param destination the array to hold the conversion
  402.      * @param destOffset the index where output will be put
  403.      * @return the number of decoded bytes converted
  404.      * @since 1.3
  405.      */
  406.     private static int decode4to3(byte[] source, int srcOffset, byte[] destination, int destOffset)
  407.     {
  408.         if(logger.isDebugEnabled())
  409.             logger.debug("decode4to3(source=" + source + ", srcOffset=" + srcOffset + ", destination=" + destination
  410.                 + ", destOffset=" + destOffset + ") - start");

  411.         // Example: Dk==
  412.         if (source[srcOffset + 2] == EQUALS_SIGN)
  413.         {
  414.             int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
  415.                     | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12);

  416.             destination[destOffset] = (byte)(outBuff >>> 16);
  417.             return 1;
  418.         }

  419.         // Example: DkL=
  420.         else if (source[srcOffset + 3] == EQUALS_SIGN)
  421.         {
  422.             int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
  423.                     | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
  424.                     | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18);

  425.             destination[destOffset] = (byte)(outBuff >>> 16);
  426.             destination[destOffset + 1] = (byte)(outBuff >>> 8);
  427.             return 2;
  428.         }

  429.         // Example: DkLE
  430.         else
  431.         {
  432.             int outBuff = ((DECODABET[source[srcOffset]] << 24) >>> 6)
  433.                     | ((DECODABET[source[srcOffset + 1]] << 24) >>> 12)
  434.                     | ((DECODABET[source[srcOffset + 2]] << 24) >>> 18)
  435.                     | ((DECODABET[source[srcOffset + 3]] << 24) >>> 24);

  436.             destination[destOffset] = (byte)(outBuff >> 16);
  437.             destination[destOffset + 1] = (byte)(outBuff >> 8);
  438.             destination[destOffset + 2] = (byte)(outBuff);
  439.             return 3;
  440.         }
  441.     }   // end decodeToBytes


  442.     /**
  443.      * Decodes data from Base64 notation.
  444.      *
  445.      * @param s the string to decode
  446.      * @return the decoded data
  447.      * @since 1.4
  448.      */
  449.     public static byte[] decode(String s)
  450.     {
  451.         logger.debug("decode(s={}) - start", s);

  452.         byte[] bytes = s.getBytes();
  453.         return decode(bytes, 0, bytes.length);
  454.     }   // end decode


  455.     /**
  456.      * Decodes data from Base64 notation and
  457.      * returns it as a string.
  458.      * Equivalent to calling
  459.      * <code>new String( decode( s ) )</code>
  460.      *
  461.      * @param s the string to decode
  462.      * @return The data as a string
  463.      * @since 1.4
  464.      */
  465.     public static String decodeToString(String s)
  466.     {
  467.         logger.debug("decodeToString(s={}) - start", s);

  468.         return new String(decode(s));
  469.     }   // end decodeToString


  470.     /**
  471.      * Attempts to decode Base64 data and deserialize a Java
  472.      * Object within. Returns <tt>null</tt> if there was an error.
  473.      *
  474.      * @param encodedObject The Base64 data to decode
  475.      * @return The decoded and deserialized object
  476.      * @since 1.4
  477.      */
  478.     public static Object decodeToObject(String encodedObject)
  479.     {
  480.         logger.debug("decodeToObject(encodedObject={} - start", encodedObject);

  481.         byte[] objBytes = decode(encodedObject);

  482.         java.io.ByteArrayInputStream bais = null;
  483.         java.io.ObjectInputStream ois = null;

  484.         try
  485.         {
  486.             bais = new java.io.ByteArrayInputStream(objBytes);
  487.             ois = new java.io.ObjectInputStream(bais);

  488.             return ois.readObject();
  489.         }   // end try
  490.         catch (java.io.IOException e)
  491.         {
  492.             logger.error("decodeToObject()", e);

  493.             e.printStackTrace();
  494.             return null;
  495.         }   // end catch
  496.         catch (ClassNotFoundException e)
  497.         {
  498.             logger.error("decodeToObject()", e);

  499.             e.printStackTrace();
  500.             return null;
  501.         }   // end catch
  502.         finally
  503.         {
  504.             try
  505.             {
  506.                 bais.close();
  507.             }
  508.             catch (Exception e)
  509.             {
  510.                 logger.error("decodeToObject()", e);
  511.             }
  512.             try
  513.             {
  514.                 ois.close();
  515.             }
  516.             catch (Exception e)
  517.             {
  518.                 logger.error("decodeToObject()", e);
  519.             }
  520.         }   // end finally
  521.     }   // end decodeObject


  522.     /**
  523.      * Decodes Base64 content in byte array format and returns
  524.      * the decoded byte array.
  525.      *
  526.      * @param source The Base64 encoded data
  527.      * @param off    The offset of where to begin decoding
  528.      * @param len    The length of characters to decode
  529.      * @return decoded data
  530.      * @since 1.3
  531.      */
  532.     public static byte[] decode(byte[] source, int off, int len)
  533.     {
  534.         if(logger.isDebugEnabled())
  535.             logger.debug("decode(source=" + source + ", off=" + off + ", len=" + len + ") - start");

  536.         int len34 = len * 3 / 4;
  537.         byte[] outBuff = new byte[len34]; // Upper limit on size of output
  538.         int outBuffPosn = 0;

  539.         byte[] b4 = new byte[4];
  540.         int b4Posn = 0;
  541.         int i = 0;
  542.         byte sbiCrop = 0;
  543.         byte sbiDecode = 0;
  544.         for (i = 0; i < len; i++)
  545.         {
  546.             sbiCrop = (byte)(source[i] & 0x7f); // Only the low seven bits
  547.             sbiDecode = DECODABET[sbiCrop];

  548.             if (sbiDecode >= WHITE_SPACE_ENC) // White space, Equals sign or better
  549.             {
  550.                 if (sbiDecode >= EQUALS_SIGN_ENC)
  551.                 {
  552.                     b4[b4Posn++] = sbiCrop;
  553.                     if (b4Posn > 3)
  554.                     {
  555.                         outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
  556.                         b4Posn = 0;

  557.                         // If that was the equals sign, break out of 'for' loop
  558.                         if (sbiCrop == EQUALS_SIGN)
  559.                             break;
  560.                     }   // end if: quartet built

  561.                 }   // end if: equals sign or better

  562.             }   // end if: white space, equals sign or better
  563.             else
  564.             {
  565.                 System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
  566.                 return null;
  567.             }   // end else:
  568.         }   // each input character

  569.         byte[] out = new byte[outBuffPosn];
  570.         System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
  571.         return out;
  572.     }   // end decode




  573.     /* ********  I N N E R   C L A S S   I N P U T S T R E A M  ******** */



  574.     /**
  575.      * A {@link Base64.InputStream} will read data from another
  576.      * {@link java.io.InputStream}, given in the constructor,
  577.      * and encode/decode to/from Base64 notation on the fly.
  578.      *
  579.      * @see Base64
  580.      * @see java.io.FilterInputStream
  581.      * @since 1.3
  582.      */
  583.     public static class InputStream extends java.io.FilterInputStream
  584.     {

  585.         /**
  586.          * Logger for this class
  587.          */
  588.         private static final Logger logger = LoggerFactory.getLogger(InputStream.class);

  589.         private boolean encode;         // Encoding or decoding
  590.         private int position;       // Current position in the buffer
  591.         private byte[] buffer;         // Small buffer holding converted data
  592.         private int bufferLength;   // Length of buffer (3 or 4)
  593.         private int numSigBytes;    // Number of meaningful bytes in the buffer


  594.         /**
  595.          * Constructs a {@link Base64.InputStream} in DECODE mode.
  596.          *
  597.          * @param in the {@link java.io.InputStream} from which to read data.
  598.          * @since 1.3
  599.          */
  600.         public InputStream(java.io.InputStream in)
  601.         {
  602.             this(in, Base64.DECODE);
  603.         }   // end constructor


  604.         /**
  605.          * Constructs a {@link Base64.InputStream} in
  606.          * either ENCODE or DECODE mode.
  607.          *
  608.          * @param in the {@link java.io.InputStream} from which to read data.
  609.          * @param encode Conversion direction
  610.          * @see Base64#ENCODE
  611.          * @see Base64#DECODE
  612.          * @since 1.3
  613.          */
  614.         public InputStream(java.io.InputStream in, boolean encode)
  615.         {
  616.             super(in);
  617.             this.encode = encode;
  618.             this.bufferLength = encode ? 4 : 3;
  619.             this.buffer = new byte[bufferLength];
  620.             this.position = -1;
  621.         }   // end constructor

  622.         /**
  623.          * Reads enough of the input stream to convert
  624.          * to/from Base64 and returns the next byte.
  625.          *
  626.          * @return next byte
  627.          * @since 1.3
  628.          */
  629.         public int read() throws java.io.IOException
  630.         {
  631.             logger.debug("read() - start");

  632.             // Do we need to get data?
  633.             if (position < 0)
  634.             {
  635.                 if (encode)
  636.                 {
  637.                     byte[] b3 = new byte[3];
  638.                     numSigBytes = 0;
  639.                     for (int i = 0; i < 3; i++)
  640.                     {
  641.                         try
  642.                         {
  643.                             int b = in.read();

  644.                             // If end of stream, b is -1.
  645.                             if (b >= 0)
  646.                             {
  647.                                 b3[i] = (byte)b;
  648.                                 numSigBytes++;
  649.                             }   // end if: not end of stream

  650.                         }   // end try: read
  651.                         catch (java.io.IOException e)
  652.                         {
  653.                             logger.error("read()", e);

  654.                             // Only a problem if we got no data at all.
  655.                             if (i == 0)
  656.                                 throw e;

  657.                         }   // end catch
  658.                     }   // end for: each needed input byte

  659.                     if (numSigBytes > 0)
  660.                     {
  661.                         encode3to4(b3, 0, numSigBytes, buffer, 0);
  662.                         position = 0;
  663.                     }   // end if: got data
  664.                 }   // end if: encoding

  665.                 // Else decoding
  666.                 else
  667.                 {
  668.                     byte[] b4 = new byte[4];
  669.                     int i = 0;
  670.                     for (i = 0; i < 4; i++)
  671.                     {
  672.                         int b = 0;
  673.                         do
  674.                         {
  675.                             b = in.read();
  676.                         }
  677.                         while (b >= 0 && DECODABET[b & 0x7f] < WHITE_SPACE_ENC);

  678.                         if (b < 0)
  679.                             break; // Reads a -1 if end of stream

  680.                         b4[i] = (byte)b;
  681.                     }   // end for: each needed input byte

  682.                     if (i == 4)
  683.                     {
  684.                         numSigBytes = decode4to3(b4, 0, buffer, 0);
  685.                         position = 0;
  686.                     }   // end if: got four characters

  687.                 }   // end else: decode
  688.             }   // end else: get data

  689.             // Got data?
  690.             if (position >= 0)
  691.             {
  692.                 // End of relevant data?
  693.                 if (position >= numSigBytes)
  694.                     return -1;

  695.                 int b = buffer[position++];

  696.                 if (position >= bufferLength)
  697.                     position = -1;

  698.                 return b;
  699.             }   // end if: position >= 0

  700.             // Else error
  701.             else
  702.                 return -1;
  703.         }   // end read


  704.         /**
  705.          * Calls {@link #read} repeatedly until the end of stream
  706.          * is reached or <var>len</var> bytes are read.
  707.          * Returns number of bytes read into array or -1 if
  708.          * end of stream is encountered.
  709.          *
  710.          * @param dest array to hold values
  711.          * @param off offset for array
  712.          * @param len max number of bytes to read into array
  713.          * @return bytes read into array or -1 if end of stream is encountered.
  714.          * @since 1.3
  715.          */
  716.         public int read(byte[] dest, int off, int len) throws java.io.IOException
  717.         {
  718.             if(logger.isDebugEnabled())
  719.                 logger.debug("read(dest=" + dest + ", off=" + off + ", len=" + len + ") - start");

  720.             int i;
  721.             int b;
  722.             for (i = 0; i < len; i++)
  723.             {
  724.                 b = read();

  725.                 if (b < 0)
  726.                     return -1;

  727.                 dest[off + i] = (byte)b;
  728.             }   // end for: each byte read
  729.             return i;
  730.         }   // end read

  731.     }   // end inner class InputStream






  732.     /* ********  I N N E R   C L A S S   O U T P U T S T R E A M  ******** */



  733.     /**
  734.      * A {@link Base64.OutputStream} will write data to another
  735.      * {@link java.io.OutputStream}, given in the constructor,
  736.      * and encode/decode to/from Base64 notation on the fly.
  737.      *
  738.      * @see Base64
  739.      * @see java.io.FilterOutputStream
  740.      * @since 1.3
  741.      */
  742.     public static class OutputStream extends java.io.FilterOutputStream
  743.     {

  744.         /**
  745.          * Logger for this class
  746.          */
  747.         private static final Logger logger = LoggerFactory.getLogger(OutputStream.class);

  748.         private boolean encode;
  749.         private int position;
  750.         private byte[] buffer;
  751.         private int bufferLength;
  752.         private int lineLength;


  753.         /**
  754.          * Constructs a {@link Base64.OutputStream} in ENCODE mode.
  755.          *
  756.          * @param out the {@link java.io.OutputStream} to which data will be written.
  757.          * @since 1.3
  758.          */
  759.         public OutputStream(java.io.OutputStream out)
  760.         {
  761.             this(out, Base64.ENCODE);
  762.         }   // end constructor


  763.         /**
  764.          * Constructs a {@link Base64.OutputStream} in
  765.          * either ENCODE or DECODE mode.
  766.          *
  767.          * @param out the {@link java.io.OutputStream} to which data will be written.
  768.          * @param encode Conversion direction
  769.          * @see Base64#ENCODE
  770.          * @see Base64#DECODE
  771.          * @since 1.3
  772.          */
  773.         public OutputStream(java.io.OutputStream out, boolean encode)
  774.         {
  775.             super(out);
  776.             this.encode = encode;
  777.             this.bufferLength = encode ? 3 : 4;
  778.             this.buffer = new byte[bufferLength];
  779.             this.position = 0;
  780.             this.lineLength = 0;
  781.         }   // end constructor


  782.         /**
  783.          * Writes the byte to the output stream after
  784.          * converting to/from Base64 notation.
  785.          * When encoding, bytes are buffered three
  786.          * at a time before the output stream actually
  787.          * gets a write() call.
  788.          * When decoding, bytes are buffered four
  789.          * at a time.
  790.          *
  791.          * @param theByte the byte to write
  792.          * @since 1.3
  793.          */
  794.         public void write(int theByte) throws java.io.IOException
  795.         {
  796.             if(logger.isDebugEnabled())
  797.                 logger.debug("write(theByte=" + theByte + ") - start");

  798.             buffer[position++] = (byte)theByte;
  799.             if (position >= bufferLength)
  800.             {
  801.                 if (encode)
  802.                 {
  803.                     out.write(Base64.encode3to4(buffer, bufferLength));

  804.                     lineLength += 4;
  805.                     if (lineLength >= MAX_LINE_LENGTH)
  806.                     {
  807.                         out.write(NEW_LINE);
  808.                         lineLength = 0;
  809.                     }   // end if: end o fline
  810.                 }   // end if: encoding
  811.                 else
  812.                     out.write(Base64.decode4to3(buffer));

  813.                 position = 0;
  814.             }   // end if: convert and flush
  815.         }   // end write


  816.         /**
  817.          * Calls {@link #write} repeatedly until <var>len</var>
  818.          * bytes are written.
  819.          *
  820.          * @param theBytes array from which to read bytes
  821.          * @param off offset for array
  822.          * @param len max number of bytes to read into array
  823.          * @since 1.3
  824.          */
  825.         public void write(byte[] theBytes, int off, int len) throws java.io.IOException
  826.         {
  827.             if(logger.isDebugEnabled())
  828.                 logger.debug("write(theBytes=" + theBytes + ", off=" + off + ", len=" + len + ") - start");

  829.             for (int i = 0; i < len; i++)
  830.             {
  831.                 write(theBytes[off + i]);
  832.             }   // end for: each byte written

  833.         }   // end write


  834.         /**
  835.          * Appropriately pads Base64 notation when encoding
  836.          * or throws an exception if Base64 input is not
  837.          * properly padded when decoding.
  838.          *
  839.          * @since 1.3
  840.          */
  841.         public void flush() throws java.io.IOException
  842.         {
  843.             logger.debug("flush() - start");

  844.             if (position > 0)
  845.             {
  846.                 if (encode)
  847.                 {
  848.                     out.write(Base64.encode3to4(buffer, position));
  849.                 }   // end if: encoding
  850.                 else
  851.                 {
  852.                     throw new java.io.IOException("Base64 input not properly padded.");
  853.                 }   // end else: decoding
  854.             }   // end if: buffer partially full

  855.             super.flush();
  856.             out.flush();
  857.         }   // end flush


  858.         /**
  859.          * Flushes and closes stream.
  860.          *
  861.          * @since 1.3
  862.          */
  863.         public void close() throws java.io.IOException
  864.         {
  865.             logger.debug("close() - start");

  866.             this.flush();

  867.             super.close();
  868.             out.close();

  869.             buffer = null;
  870.             out = null;
  871.         }   // end close

  872.     }   // end inner class OutputStream


  873. }   // end class Base64