Source for org.jfree.xml.util.Base64

   1: /* ========================================================================
   2:  * JCommon : a free general purpose class library for the Java(tm) platform
   3:  * ========================================================================
   4:  *
   5:  * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
   6:  * 
   7:  * Project Info:  http://www.jfree.org/jcommon/index.html
   8:  *
   9:  * This library is free software; you can redistribute it and/or modify it 
  10:  * under the terms of the GNU Lesser General Public License as published by 
  11:  * the Free Software Foundation; either version 2.1 of the License, or 
  12:  * (at your option) any later version.
  13:  *
  14:  * This library is distributed in the hope that it will be useful, but 
  15:  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  16:  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
  17:  * License for more details.
  18:  *
  19:  * You should have received a copy of the GNU Lesser General Public
  20:  * License along with this library; if not, write to the Free Software
  21:  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
  22:  * USA.  
  23:  *
  24:  * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
  25:  * in the United States and other countries.]
  26:  * 
  27:  * -------------------------------------
  28:  * AbstractElementDefinitionHandler.java
  29:  * -------------------------------------
  30:  * (C)opyright 2003-2005, by Thomas Morgner and Contributors.
  31:  *
  32:  * Original Author:  Kevin Kelley <kelley@ruralnet.net> -
  33:  *                   30718 Rd. 28, La Junta, CO, 81050  USA. 
  34:  *
  35:  * $Id: Base64.java,v 1.4 2005/10/18 13:33:53 mungady Exp $
  36:  *
  37:  * Changes
  38:  * -------------------------
  39:  * 23.09.2003 : Initial version
  40:  *
  41:  */
  42: package org.jfree.xml.util;
  43: 
  44: import java.io.BufferedInputStream;
  45: import java.io.BufferedOutputStream;
  46: import java.io.BufferedReader;
  47: import java.io.BufferedWriter;
  48: import java.io.ByteArrayOutputStream;
  49: import java.io.CharArrayWriter;
  50: import java.io.File;
  51: import java.io.FileInputStream;
  52: import java.io.FileOutputStream;
  53: import java.io.FileReader;
  54: import java.io.FileWriter;
  55: import java.io.InputStream;
  56: import java.io.OutputStream;
  57: import java.io.Reader;
  58: import java.io.Writer;
  59: 
  60: /**
  61:  * Provides encoding of raw bytes to base64-encoded characters, and
  62:  * decoding of base64 characters to raw bytes.
  63:  * date: 06 August 1998
  64:  * modified: 14 February 2000
  65:  * modified: 22 September 2000
  66:  *
  67:  * @author Kevin Kelley (kelley@ruralnet.net)
  68:  * @version 1.3
  69:  */
  70: public class Base64 {
  71: 
  72:   private Base64 ()
  73:   {
  74:   }
  75: 
  76:     /**
  77:      * returns an array of base64-encoded characters to represent the
  78:      * passed data array.
  79:      *
  80:      * @param data the array of bytes to encode
  81:      * @return base64-coded character array.
  82:      */
  83:     public static char[] encode(final byte[] data) {
  84:         final char[] out = new char[((data.length + 2) / 3) * 4];
  85: 
  86:         //
  87:         // 3 bytes encode to 4 chars.  Output is always an even
  88:         // multiple of 4 characters.
  89:         //
  90:         for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
  91:             boolean quad = false;
  92:             boolean trip = false;
  93: 
  94:             int val = (0xFF & data[i]);
  95:             val <<= 8;
  96:             if ((i + 1) < data.length) {
  97:                 val |= (0xFF & data[i + 1]);
  98:                 trip = true;
  99:             }
 100:             val <<= 8;
 101:             if ((i + 2) < data.length) {
 102:                 val |= (0xFF & data[i + 2]);
 103:                 quad = true;
 104:             }
 105:             out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
 106:             val >>= 6;
 107:             out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
 108:             val >>= 6;
 109:             out[index + 1] = alphabet[val & 0x3F];
 110:             val >>= 6;
 111:             out[index + 0] = alphabet[val & 0x3F];
 112:         }
 113:         return out;
 114:     }
 115: 
 116:     /**
 117:      * Decodes a BASE-64 encoded stream to recover the original
 118:      * data. White space before and after will be trimmed away,
 119:      * but no other manipulation of the input will be performed.
 120:      *
 121:      * As of version 1.2 this method will properly handle input
 122:      * containing junk characters (newlines and the like) rather
 123:      * than throwing an error. It does this by pre-parsing the
 124:      * input and generating from that a count of VALID input
 125:      * characters.
 126:      * 
 127:      * @param data  the character data.
 128:      * 
 129:      * @return The decoded data.
 130:      */
 131:     public static byte[] decode(final char[] data) {
 132:         // as our input could contain non-BASE64 data (newlines,
 133:         // whitespace of any sort, whatever) we must first adjust
 134:         // our count of USABLE data so that...
 135:         // (a) we don't misallocate the output array, and
 136:         // (b) think that we miscalculated our data length
 137:         //     just because of extraneous throw-away junk
 138: 
 139:         int tempLen = data.length;
 140:         for (int ix = 0; ix < data.length; ix++) {
 141:             if ((data[ix] > 255) || codes[data[ix]] < 0) {
 142:                 --tempLen; // ignore non-valid chars and padding
 143:             }
 144:         }
 145:         // calculate required length:
 146:         //  -- 3 bytes for every 4 valid base64 chars
 147:         //  -- plus 2 bytes if there are 3 extra base64 chars,
 148:         //     or plus 1 byte if there are 2 extra.
 149: 
 150:         int len = (tempLen / 4) * 3;
 151:         if ((tempLen % 4) == 3) {
 152:             len += 2;
 153:         }
 154:         if ((tempLen % 4) == 2) {
 155:             len += 1;
 156:         }
 157: 
 158:         final byte[] out = new byte[len];
 159: 
 160: 
 161:         int shift = 0; // # of excess bits stored in accum
 162:         int accum = 0; // excess bits
 163:         int index = 0;
 164: 
 165:         // we now go through the entire array (NOT using the 'tempLen' value)
 166:         for (int ix = 0; ix < data.length; ix++) {
 167:             final int value = (data[ix] > 255) ? -1 : codes[data[ix]];
 168: 
 169:             if (value >= 0) { // skip over non-code
 170:                 accum <<= 6; // bits shift up by 6 each time thru
 171:                 shift += 6; // loop, with new bits being put in
 172:                 accum |= value; // at the bottom.
 173:                 if (shift >= 8) { // whenever there are 8 or more shifted in,
 174:                     shift -= 8; // write them out (from the top, leaving any
 175:                     out[index++] = // excess at the bottom for next iteration.
 176:                         (byte) ((accum >> shift) & 0xff);
 177:                 }
 178:             }
 179:             // we will also have skipped processing a padding null byte ('=') here;
 180:             // these are used ONLY for padding to an even length and do not legally
 181:             // occur as encoded data. for this reason we can ignore the fact that
 182:             // no index++ operation occurs in that special case: the out[] array is
 183:             // initialized to all-zero bytes to start with and that works to our
 184:             // advantage in this combination.
 185:         }
 186: 
 187:         // if there is STILL something wrong we just have to throw up now!
 188:         if (index != out.length) {
 189:             throw new Error("Miscalculated data length (wrote " 
 190:                 + index + " instead of " + out.length + ")");
 191:         }
 192: 
 193:         return out;
 194:     }
 195: 
 196: 
 197:     //
 198:     // code characters for values 0..63
 199:     //
 200:     private static char[] alphabet =
 201:         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
 202: 
 203:     //
 204:     // lookup table for converting base64 characters to value in range 0..63
 205:     //
 206:     private static byte[] codes = new byte[256];
 207: 
 208:     static {
 209:         for (int i = 0; i < 256; i++) {
 210:             codes[i] = -1;
 211:         }
 212:         for (int i = 'A'; i <= 'Z'; i++) {
 213:             codes[i] = (byte) (i - 'A');
 214:         }
 215:         for (int i = 'a'; i <= 'z'; i++) {
 216:             codes[i] = (byte) (26 + i - 'a');
 217:         }
 218:         for (int i = '0'; i <= '9'; i++) {
 219:             codes[i] = (byte) (52 + i - '0');
 220:         }
 221:         codes['+'] = 62;
 222:         codes['/'] = 63;
 223:     }
 224: 
 225: 
 226: 
 227: 
 228:     ///////////////////////////////////////////////////
 229:     // remainder (main method and helper functions) is
 230:     // for testing purposes only, feel free to clip it.
 231:     ///////////////////////////////////////////////////
 232: 
 233:     /**
 234:      * Entry point.
 235:      * 
 236:      * @param args  the command line arguments.
 237:      */
 238:     public static void main(final String[] args) {
 239:         boolean decode = false;
 240: 
 241:         if (args.length == 0) {
 242:             System.out.println("usage:  java Base64 [-d[ecode]] filename");
 243:             System.exit(0);
 244:         }
 245:         for (int i = 0; i < args.length; i++) {
 246:             if ("-decode".equalsIgnoreCase(args[i])) {
 247:                 decode = true;
 248:             }
 249:             else if ("-d".equalsIgnoreCase(args[i])) {
 250:                 decode = true;
 251:             }   
 252:         }
 253: 
 254:         final String filename = args[args.length - 1];
 255:         final File file = new File(filename);
 256:         if (!file.exists()) {
 257:             System.out.println("Error:  file '" + filename + "' doesn't exist!");
 258:             System.exit(0);
 259:         }
 260: 
 261:         if (decode) {
 262:             final char[] encoded = readChars(file);
 263:             final byte[] decoded = decode(encoded);
 264:             writeBytes(file, decoded);
 265:         }
 266:         else {
 267:             final byte[] decoded = readBytes(file);
 268:             final char[] encoded = encode(decoded);
 269:             writeChars(file, encoded);
 270:         }
 271:     }
 272: 
 273:     private static byte[] readBytes(final File file) {
 274:         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
 275:         try {
 276:             final InputStream fis = new FileInputStream(file);
 277:             final InputStream is = new BufferedInputStream(fis);
 278: 
 279:             int count;
 280:             final byte[] buf = new byte[16384];
 281:             while ((count = is.read(buf)) != -1) {
 282:                 if (count > 0) {
 283:                     baos.write(buf, 0, count);
 284:                 }   
 285:             }
 286:             is.close();
 287:         }
 288:         catch (Exception e) {
 289:             e.printStackTrace();
 290:         }
 291: 
 292:         return baos.toByteArray();
 293:     }
 294: 
 295:     private static char[] readChars(final File file) {
 296:         final CharArrayWriter caw = new CharArrayWriter();
 297:         try {
 298:             final Reader fr = new FileReader(file);
 299:             final Reader in = new BufferedReader(fr);
 300:             int count;
 301:             final char[] buf = new char[16384];
 302:             while ((count = in.read(buf)) != -1) {
 303:                 if (count > 0) {
 304:                     caw.write(buf, 0, count);
 305:                 }
 306:             }
 307:             in.close();
 308:         }
 309:         catch (Exception e) {
 310:             e.printStackTrace();
 311:         }
 312: 
 313:         return caw.toCharArray();
 314:     }
 315: 
 316:     private static void writeBytes(final File file, final byte[] data) {
 317:         try {
 318:             final OutputStream fos = new FileOutputStream(file);
 319:             final OutputStream os = new BufferedOutputStream(fos);
 320:             os.write(data);
 321:             os.close();
 322:         }
 323:         catch (Exception e) {
 324:             e.printStackTrace();
 325:         }
 326:     }
 327: 
 328:     private static void writeChars(final File file, final char[] data) {
 329:         try {
 330:             final Writer fos = new FileWriter(file);
 331:             final Writer os = new BufferedWriter(fos);
 332:             os.write(data);
 333:             os.close();
 334:         }
 335:         catch (Exception e) {
 336:             e.printStackTrace();
 337:         }
 338:     }
 339:     ///////////////////////////////////////////////////
 340:     // end of test code.
 341:     ///////////////////////////////////////////////////
 342: 
 343: }