001/* ======================================================================== 002 * JCommon : a free general purpose class library for the Java(tm) platform 003 * ======================================================================== 004 * 005 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors. 006 * 007 * Project Info: http://www.jfree.org/jcommon/index.html 008 * 009 * This library is free software; you can redistribute it and/or modify it 010 * under the terms of the GNU Lesser General Public License as published by 011 * the Free Software Foundation; either version 2.1 of the License, or 012 * (at your option) any later version. 013 * 014 * This library is distributed in the hope that it will be useful, but 015 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 016 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 017 * License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this library; if not, write to the Free Software 021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 022 * USA. 023 * 024 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 025 * in the United States and other countries.] 026 * 027 * ------------------------------------- 028 * AbstractElementDefinitionHandler.java 029 * ------------------------------------- 030 * (C)opyright 2003-2005, by Thomas Morgner and Contributors. 031 * 032 * Original Author: Kevin Kelley <kelley@ruralnet.net> - 033 * 30718 Rd. 28, La Junta, CO, 81050 USA. 034 * 035 * $Id: Base64.java,v 1.4 2005/10/18 13:33:53 mungady Exp $ 036 * 037 * Changes 038 * ------------------------- 039 * 23.09.2003 : Initial version 040 * 041 */ 042package org.jfree.xml.util; 043 044import java.io.BufferedInputStream; 045import java.io.BufferedOutputStream; 046import java.io.BufferedReader; 047import java.io.BufferedWriter; 048import java.io.ByteArrayOutputStream; 049import java.io.CharArrayWriter; 050import java.io.File; 051import java.io.FileInputStream; 052import java.io.FileOutputStream; 053import java.io.FileReader; 054import java.io.FileWriter; 055import java.io.InputStream; 056import java.io.OutputStream; 057import java.io.Reader; 058import java.io.Writer; 059 060/** 061 * Provides encoding of raw bytes to base64-encoded characters, and 062 * decoding of base64 characters to raw bytes. 063 * date: 06 August 1998 064 * modified: 14 February 2000 065 * modified: 22 September 2000 066 * 067 * @author Kevin Kelley (kelley@ruralnet.net) 068 * @version 1.3 069 */ 070public class Base64 { 071 072 private Base64 () 073 { 074 } 075 076 /** 077 * returns an array of base64-encoded characters to represent the 078 * passed data array. 079 * 080 * @param data the array of bytes to encode 081 * @return base64-coded character array. 082 */ 083 public static char[] encode(final byte[] data) { 084 final char[] out = new char[((data.length + 2) / 3) * 4]; 085 086 // 087 // 3 bytes encode to 4 chars. Output is always an even 088 // multiple of 4 characters. 089 // 090 for (int i = 0, index = 0; i < data.length; i += 3, index += 4) { 091 boolean quad = false; 092 boolean trip = false; 093 094 int val = (0xFF & data[i]); 095 val <<= 8; 096 if ((i + 1) < data.length) { 097 val |= (0xFF & data[i + 1]); 098 trip = true; 099 } 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}