Coverage Report - org.webmacro.util.Base64
 
Classes in this File Line Coverage Branch Coverage Complexity
Base64
67%
74/110
54%
26/48
6.75
 
 1  
 /*
 2  
  * Copyright (C) 1998-2000 Semiotek Inc.  All Rights Reserved.
 3  
  *
 4  
  * Redistribution and use in source and binary forms, with or without
 5  
  * modification, are permitted under the terms of either of the following
 6  
  * Open Source licenses:
 7  
  *
 8  
  * The GNU General Public License, version 2, or any later version, as
 9  
  * published by the Free Software Foundation
 10  
  * (http://www.fsf.org/copyleft/gpl.html);
 11  
  *
 12  
  *  or
 13  
  *
 14  
  * The Semiotek Public License (http://webmacro.org/LICENSE.)
 15  
  *
 16  
  * This software is provided "as is", with NO WARRANTY, not even the
 17  
  * implied warranties of fitness to purpose, or merchantability. You
 18  
  * assume all risks and liabilities associated with its use.
 19  
  *
 20  
  * See www.webmacro.org for more information on the WebMacro project.
 21  
  */
 22  
 
 23  
 
 24  
 package org.webmacro.util;
 25  
 
 26  
 /**
 27  
  * A set of utility methods for working with Base64.
 28  
  *
 29  
  */
 30  
 final public class Base64
 31  
 {
 32  
 
 33  
     private static final byte UPPER_FOUR = (byte) (16 + 32 + 64 + 128);
 34  
     private static final byte LOWER_FOUR = (byte) (1 + 2 + 4 + 8);
 35  
     private static final byte UPPER_SIX = (byte) (4 + 8 + 16 + 32 + 64 + 128);
 36  
     private static final byte LOWER_TWO = (byte) (1 + 2);
 37  
     private static final byte UPPER_TWO = (byte) (64 + 128);
 38  
     private static final byte LOWER_SIX = (byte) (1 + 2 + 4 + 8 + 16 + 32);
 39  
 
 40  
     /* Disallow instantiation. */
 41  0
     private Base64() {}
 42  
     
 43  
     /**
 44  
      * Get the plain text version of a base64 encoded string.
 45  
      */
 46  
 
 47  
     final public static String decode (String encoded)
 48  
     {
 49  5
         return decode(encoded.getBytes());
 50  
     }
 51  
 
 52  
     /**
 53  
      * Get the base64 encoded version of a plain text String
 54  
      */
 55  
     final public static String encode (String plainText)
 56  
     {
 57  3
         return encode(plainText.getBytes());
 58  
     }
 59  
 
 60  
     /**
 61  
      * Ge tthe plain text version of a base64 encoded byte array
 62  
      */
 63  
     final public static String decode (byte[] encoded)
 64  
     {
 65  8
         byte[] plain = new byte[(int) (encoded.length * 0.75) + 2];
 66  
 
 67  
         byte code, ptext;
 68  7
         int ppos = 0;
 69  7
         int epos = 0;
 70  7
         boolean cutShort = false;
 71  13
         while (epos < encoded.length)
 72  
         {
 73  
 
 74  
             /*
 75  
              * base64 decoding: turn 4*6 bits into 3*8 bits via the pattern:
 76  
              * xx111111 xx112222 xx222233 xx333333    (xx: high bits unused)
 77  
              */
 78  
 
 79  7
             code = sixBits(encoded[epos]);
 80  
 
 81  
             // 1st byte: lower six bits from 1st char
 82  6
             ptext = (byte) (code << 2);
 83  
 
 84  
             // 1st byte: plus upper two (of six) bits from 2nd char
 85  6
             epos++;
 86  
             try
 87  
             {
 88  6
                 code = sixBits(encoded[epos]);
 89  
             }
 90  0
             catch (Exception e)
 91  
             {
 92  0
                 code = 0; // too few chars, assume missing pad
 93  0
                 cutShort = true;
 94  6
             }
 95  6
             ptext |= (code & UPPER_FOUR) >>> 4;
 96  6
             plain[ppos++] = ptext;
 97  6
             if (cutShort)
 98  
             {
 99  0
                 break; // loop
 100  
             }
 101  
 
 102  
 
 103  
             // 2nd byte: lower four bytes from 2nd char
 104  6
             ptext = (byte) ((code & LOWER_FOUR) << 4);
 105  
 
 106  
             // 2nd byte: plus upper four (of six) bits from 3rd char
 107  6
             epos++;
 108  
             try
 109  
             {
 110  6
                 code = sixBits(encoded[epos]);
 111  
             }
 112  0
             catch (Exception e)
 113  
             {
 114  0
                 code = 0; // too few chars, assume missing pad
 115  6
             }
 116  6
             ptext |= (code & UPPER_SIX) >>> 2;
 117  6
             plain[ppos++] = ptext;
 118  6
             if (cutShort)
 119  
             {
 120  0
                 break; // loop
 121  
             }
 122  
 
 123  
             // 3rd byte: lower two bits of 3rd char
 124  6
             ptext = (byte) ((code & LOWER_TWO) << 6);
 125  
 
 126  
             // 3rd byte: lower six bits of fourth char
 127  6
             epos++;
 128  
             try
 129  
             {
 130  6
                 code = sixBits(encoded[epos]);
 131  
             }
 132  0
             catch (Exception e)
 133  
             {
 134  0
                 code = 0; // too few chars, assume missing pad
 135  6
             }
 136  6
             ptext |= code;
 137  6
             plain[ppos++] = ptext;
 138  
 
 139  
 
 140  
             // advance loop
 141  6
             epos++;
 142  
         }
 143  6
         return new String(plain);
 144  
     }
 145  
 
 146  
     /**
 147  
      * Get the base64 encoded version of a plain text byte array.
 148  
      */
 149  
     final static public String encode (byte[] plain)
 150  
     {
 151  
 
 152  
         /*
 153  
          * base64 encoding: turn 3*8 bits into 4*6 bits via the pattern:
 154  
          * 111111 112222 222233 333333    (xx: high bits unused)
 155  
          * UPPER6 LOWER2 LOWER4 LOWER6
 156  
          *        UPPER4 UPPER2
 157  
         **/
 158  
 
 159  7
         StringBuffer encoded = new StringBuffer((int) (plain.length * 1.34) + 1);
 160  
 
 161  
         byte ptext;
 162  
         byte sixbits;
 163  7
         int ppos = 0;
 164  7
         boolean cutShort = false;
 165  12
         while (ppos < plain.length)
 166  
         {
 167  
             // first char: upper 6 bytes
 168  7
             ptext = plain[ppos];
 169  7
             sixbits = (byte) ((ptext & UPPER_SIX) >>> 2);
 170  7
             encoded.append(base64(sixbits));
 171  
 
 172  
             // second char: lower 2, upper 4
 173  7
             sixbits = (byte) ((ptext & LOWER_TWO) << 4);
 174  
 
 175  7
             ppos++;
 176  
             try
 177  
             {
 178  7
                 ptext = plain[ppos];
 179  
             }
 180  0
             catch (ArrayIndexOutOfBoundsException e)
 181  
             {
 182  0
                 ptext = 0;
 183  0
                 cutShort = true;
 184  7
             }
 185  7
             sixbits |= (byte) ((ptext & UPPER_FOUR) >>> 4);
 186  7
             encoded.append(base64(sixbits));
 187  7
             if (cutShort)
 188  
             {
 189  0
                 encoded.append("==");
 190  0
                 return encoded.toString();
 191  
             }
 192  
 
 193  
             // third char: lower four, upper 2
 194  7
             sixbits = (byte) ((ptext & LOWER_FOUR) << 2);
 195  7
             ppos++;
 196  
             try
 197  
             {
 198  7
                 ptext = plain[ppos];
 199  
             }
 200  2
             catch (ArrayIndexOutOfBoundsException e)
 201  
             {
 202  2
                 ptext = 0;
 203  2
                 cutShort = true;
 204  5
             }
 205  7
             sixbits |= (byte) ((ptext & UPPER_TWO) >>> 6);
 206  7
             encoded.append(base64(sixbits));
 207  7
             if (cutShort)
 208  
             {
 209  2
                 encoded.append("=");
 210  2
                 return encoded.toString();
 211  
             }
 212  
 
 213  
             // fourth char: lower six
 214  5
             sixbits = (byte) (ptext & LOWER_SIX);
 215  5
             encoded.append(base64(sixbits));
 216  
 
 217  
             // increment loop
 218  5
             ppos++;
 219  
         }
 220  5
         return encoded.toString();
 221  
     }
 222  
 
 223  
 
 224  
     /**
 225  
      * Translate a character in the base64 alphabet into a byte with
 226  
      * the corresponding bits set (ie: a number from 0 to 64).
 227  
      * @return the base64 value, or 0 for the special '=' pad character
 228  
      * @param base64 the character to be translated
 229  
      * @exception NumberFormatException if base64 is not a base64 character
 230  
      */
 231  
     private static byte sixBits (byte base64)
 232  
     {
 233  
 
 234  25
         if ((base64 >= 'A') && (base64 <= 'Z'))
 235  
         {
 236  8
             return (byte) (base64 - 'A');
 237  
         }
 238  
 
 239  17
         if ((base64 >= 'a') && (base64 <= 'z'))
 240  
         {
 241  14
             return (byte) (base64 - 'a' + 26);
 242  
         }
 243  
 
 244  3
         if ((base64 >= '0') && (base64 <= '9'))
 245  
         {
 246  0
             return (byte) (base64 - '0' + 52);
 247  
         }
 248  
 
 249  3
         if (base64 == '+')
 250  
         {
 251  0
             return 62;
 252  
         }
 253  
 
 254  3
         if (base64 == '/')
 255  
         {
 256  0
             return 63;
 257  
         }
 258  
 
 259  3
         if (base64 == '=')
 260  
         {
 261  2
             return 0; // pad means zeroed bits FIXME: i am not sure, really?
 262  
         }
 263  1
         throw new NumberFormatException("Not a base64 character: " + base64);
 264  
     }
 265  
 
 266  
     /**
 267  
      * Turn a six-bit value into a base64 digit.
 268  
      */
 269  
     static private char base64 (byte sixBits)
 270  
     {
 271  
 
 272  26
         if (sixBits <= 25)
 273  
         { // less/equal base64 'Z'
 274  12
             return (char) ('A' + sixBits);
 275  
         }
 276  
 
 277  14
         if (sixBits <= 51)
 278  
         { // less/equal base64 'z'
 279  14
             return (char) ('a' + sixBits - 26); // count from base64 'a'
 280  
         }
 281  
 
 282  0
         if (sixBits <= 61)
 283  
         { // less/equal base64 '9'
 284  0
             return (char) ('0' + sixBits - 52); // count from base64 '0'
 285  
         }
 286  
 
 287  0
         if (sixBits == 62)
 288  
         {
 289  0
             return '+';
 290  
         }
 291  
 
 292  0
         if (sixBits == 63)
 293  
         {
 294  0
             return '/';
 295  
         }
 296  
 
 297  0
         throw new NumberFormatException("Not a base64 digit: " + sixBits);
 298  
     }
 299  
 
 300  
 
 301  
     /**
 302  
      * Test harness.
 303  
      */
 304  
     public static void main (String arg[])
 305  
     {
 306  
 
 307  
         boolean encode;
 308  
 
 309  0
         if (arg.length < 2)
 310  
         {
 311  0
             System.out.println("Usage: Base64 encode|decode string");
 312  0
             return;
 313  
         }
 314  
 
 315  0
         if (arg[0].equals("encode"))
 316  
         {
 317  0
             encode = true;
 318  
         }
 319  0
         else if (arg[0].equals("decode"))
 320  
         {
 321  0
             encode = false;
 322  
         }
 323  
         else
 324  
         {
 325  0
             System.out.println("Unrecognized argument: " + arg[0]);
 326  0
             return;
 327  
         }
 328  
 
 329  0
         System.out.println(encode ? encode(arg[1]) : decode(arg[1]));
 330  
 
 331  0
     }
 332  
 }
 333