components/visual-panels/core/src/java/util/com/oracle/solaris/vp/util/misc/Base64Util.java
changeset 827 0944d8c0158b
equal deleted inserted replaced
826:c6aad84d2493 827:0944d8c0158b
       
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 package com.oracle.solaris.vp.util.misc;
       
    27 
       
    28 import java.io.*;
       
    29 
       
    30 public class Base64Util {
       
    31     //
       
    32     // Inner classes
       
    33     //
       
    34 
       
    35     @SuppressWarnings({"serial"})
       
    36     public static class Base64Exception extends IOException {
       
    37 	public Base64Exception(String message) {
       
    38 	    super(message);
       
    39 	}
       
    40     }
       
    41 
       
    42     /**
       
    43      * Utility class provided for callers who wish to enforce a maximum
       
    44      * line length for their Base64-encoded output.
       
    45      */
       
    46     public static class WrappingWriter extends FilterWriter {
       
    47 	private static final String NEWLINE =
       
    48 	    System.getProperty("line.separator");
       
    49 
       
    50 	private int column_ = 0;
       
    51 	private int max_ = 76;
       
    52 
       
    53 	/**
       
    54 	 * Creates a new WrappingWriter which writes line-wrapped
       
    55 	 * output to {@code out} using the default width (76
       
    56 	 * characters).
       
    57 	 *
       
    58 	 * @param out the {@code Writer} to which output should be sent
       
    59 	 */
       
    60     	public WrappingWriter(Writer out)
       
    61 	{
       
    62 	    super(out);
       
    63 	}
       
    64 
       
    65 	/**
       
    66 	 * Creates a new WrappingWriter which writes line-wrapped
       
    67 	 * output to {@code out} using the specified width.
       
    68 	 *
       
    69 	 * @param out the {@code Writer} to which output should be sent
       
    70 	 * @param max the maximum number of characters per line
       
    71 	 */
       
    72     	public WrappingWriter(Writer out, int max)
       
    73 	{
       
    74 	    super(out);
       
    75 	    max_ = max;
       
    76 	}
       
    77 
       
    78 	@Override
       
    79 	public void write(int c) throws IOException
       
    80 	{
       
    81 	    if (++column_ > max_) {
       
    82 		column_ = 1;
       
    83 		out.write(NEWLINE);
       
    84 	    }
       
    85 
       
    86 	    out.write(c);
       
    87 	}
       
    88 
       
    89 	@Override
       
    90 	public void flush() throws IOException
       
    91 	{
       
    92 	    if (column_ != 0)
       
    93 		out.write(NEWLINE);
       
    94 	    column_ = 0;
       
    95 
       
    96 	    out.flush();
       
    97 	}
       
    98 
       
    99 	/*
       
   100 	 * We currently only use write(int), so we don't need to
       
   101 	 * redefine any other methods.
       
   102 	 */
       
   103     }
       
   104 
       
   105     //
       
   106     // Static data
       
   107     //
       
   108 
       
   109     private static final char ENCODE[] = {
       
   110 	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
       
   111 	'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
       
   112 	'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
       
   113 	'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
       
   114 	'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
       
   115 	'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
       
   116 	'w', 'x', 'y', 'z', '0', '1', '2', '3',
       
   117 	'4', '5', '6', '7', '8', '9', '+', '/'
       
   118     };
       
   119 
       
   120     private static final byte DECODE[] = new byte[256];
       
   121 
       
   122     static {
       
   123 	for (int i = 0; i < DECODE.length; i++) {
       
   124 	    DECODE[i] = (byte)-1;
       
   125 	}
       
   126 	for (int i = 0; i < ENCODE.length; i++) {
       
   127 	    DECODE[ENCODE[i]] = (byte)i;
       
   128 	}
       
   129     }
       
   130 
       
   131     private static final char CHAR_PAD = '=';
       
   132 
       
   133     //
       
   134     // Static methods
       
   135     //
       
   136 
       
   137     /**
       
   138      * Decodes the base64-encoded data on the given {@code InputStream} to the
       
   139      * given {@code OutputStream}.
       
   140      *
       
   141      * @exception   Base64Exception
       
   142      *		    if the given {@code InputStream} has invalid or insufficient
       
   143      *		    data
       
   144      *
       
   145      * @exception   IOException
       
   146      *		    if an I/O error ocurrs
       
   147      */
       
   148     public static void decode(InputStream in, OutputStream out)
       
   149 	throws IOException {
       
   150 
       
   151 	// Total number of bytes read
       
   152 	int count = 0;
       
   153 
       
   154 	OUTER: while (true) {
       
   155 	    int pad = 0;
       
   156 	    int atom = 0;
       
   157 
       
   158 	    // Read in 4 characters (6 significant bits each)
       
   159 	    for (int i = 0; i < 4; i++) {
       
   160 		int b = in.read();
       
   161 
       
   162 		if (b == -1) {
       
   163 		    if (i == 0) {
       
   164 			break OUTER;
       
   165 		    }
       
   166 
       
   167 		    if (pad != 0) {
       
   168 			throw new Base64Exception(String.format(
       
   169 			    "expected '%c' at position %d, found EOS", CHAR_PAD,
       
   170 			    count));
       
   171 		    } else {
       
   172 			throw new Base64Exception(String.format(
       
   173 			    "unexpected EOS at position %d", count));
       
   174 		    }
       
   175 
       
   176 		// Ignore whitespace
       
   177 		} else if (Character.isWhitespace((char)b)) {
       
   178 		    i--;
       
   179 		    continue;
       
   180 		}
       
   181 
       
   182 		atom <<= 6;
       
   183 
       
   184 		if (b == CHAR_PAD) {
       
   185 		    // Pad chars can only be the 3rd or 4th char in the set
       
   186 		    if (i < 2) {
       
   187 			throw new Base64Exception(String.format(
       
   188 			    "invalid char at position %d: %s", count,
       
   189 			    getCharDescription((char)b)));
       
   190 		    }
       
   191 		    pad++;
       
   192 
       
   193 		} else if (pad != 0) {
       
   194 		    throw new Base64Exception(String.format(
       
   195 			"invalid char at position %d: %s (expected %s)", count,
       
   196 			getCharDescription((char)b),
       
   197 			getCharDescription(CHAR_PAD)));
       
   198 
       
   199 		} else {
       
   200 		    // InputStream.read guarantees -1 <= b <= 255
       
   201 		    int index = DECODE[b];
       
   202 
       
   203 		    if (index < 0 || index > 63) {
       
   204 			throw new Base64Exception(String.format(
       
   205 			    "invalid char at position %d: %s", count,
       
   206 			    getCharDescription((char)b)));
       
   207 		    }
       
   208 
       
   209 		    atom |= index;
       
   210 		}
       
   211 
       
   212 		count++;
       
   213 	    }
       
   214 
       
   215 	    // Output 3 8-bit bytes
       
   216 	    for (int i = 0; i < 3 - pad; i++) {
       
   217 		out.write((byte)((atom & 0x00FF0000) >>> 16));
       
   218 		atom <<= 8;
       
   219 	    }
       
   220 	}
       
   221 
       
   222 	out.flush();
       
   223     }
       
   224 
       
   225     public static byte[] decode(InputStream in)
       
   226 	throws IOException {
       
   227 
       
   228 	ByteArrayOutputStream out = new ByteArrayOutputStream();
       
   229 	decode(in, out);
       
   230 
       
   231 	return out.toByteArray();
       
   232     }
       
   233 
       
   234     public static void decode(String in, OutputStream out)
       
   235 	throws IOException {
       
   236 
       
   237 	ByteArrayInputStream bin =
       
   238 	    new ByteArrayInputStream(in.getBytes());
       
   239 
       
   240 	decode(bin, out);
       
   241     }
       
   242 
       
   243     public static byte[] decode(String in)
       
   244 	throws IOException {
       
   245 
       
   246 	ByteArrayOutputStream out = new ByteArrayOutputStream();
       
   247 	decode(in, out);
       
   248 
       
   249 	return out.toByteArray();
       
   250     }
       
   251 
       
   252     /**
       
   253      * Encodes the data on the given {@code InputStream} as base64 and writes it
       
   254      * to the given {@code Writer}.
       
   255      *
       
   256      * @exception   IOException
       
   257      *		    if an I/O error ocurrs
       
   258      */
       
   259     public static void encode(InputStream in, Writer out)
       
   260 	throws IOException {
       
   261 
       
   262 	OUTER: while (true) {
       
   263 	    int pad = 0;
       
   264 	    int atom = 0;
       
   265 
       
   266 	    // Read in 3 8-bit bytes
       
   267 	    for (int i = 0; i < 3; i++) {
       
   268 		int b = in.read();
       
   269 
       
   270 		if (b == -1) {
       
   271 		    if (i == 0) {
       
   272 			break OUTER;
       
   273 		    }
       
   274 		    pad++;
       
   275 		} else {
       
   276 		    atom |= b;
       
   277 		}
       
   278 		atom <<= 8;
       
   279 	    }
       
   280 
       
   281 	    // Output 4 6-bit characters...
       
   282 	    for (int i = 0; i < 4 - pad; i++) {
       
   283 		int index = (atom & 0xFC000000) >>> 26;
       
   284 		out.write(ENCODE[index]);
       
   285 		atom <<= 6;
       
   286 	    }
       
   287 
       
   288 	    // ...plus any padding if 3 bytes couldn't be read
       
   289 	    for (int i = 0; i < pad; i++) {
       
   290 		out.write(CHAR_PAD);
       
   291 	    }
       
   292 	}
       
   293 
       
   294 	out.flush();
       
   295     }
       
   296 
       
   297     public static void encode(byte[] buffer, int offset, int len,
       
   298 	Writer out) throws IOException {
       
   299 
       
   300 	ByteArrayInputStream in =
       
   301 	    new ByteArrayInputStream(buffer, offset, len);
       
   302 	encode(in, out);
       
   303     }
       
   304 
       
   305     public static void encode(byte[] buffer, Writer out)
       
   306 	throws IOException {
       
   307 	encode(buffer, 0, buffer.length, out);
       
   308     }
       
   309 
       
   310     public static void encode(String s, Writer out)
       
   311 	throws IOException {
       
   312 	encode(s.getBytes(), out);
       
   313     }
       
   314 
       
   315     public static String encode(byte[] buffer, int offset, int len) {
       
   316 	StringWriter out = new StringWriter();
       
   317 	try {
       
   318 	    encode(buffer, offset, len, out);
       
   319 	    return out.toString();
       
   320 	} catch (IOException e) {
       
   321 	    // Not likely -- output is from a byte array to a String
       
   322 	}
       
   323 
       
   324 	return null;
       
   325     }
       
   326 
       
   327     public static String encode(byte[] buffer) {
       
   328 	return encode(buffer, 0, buffer.length);
       
   329     }
       
   330 
       
   331     public static String encode(String s) {
       
   332 	return encode(s.getBytes());
       
   333     }
       
   334 
       
   335     //
       
   336     // Private static methods
       
   337     //
       
   338 
       
   339     private static String getCharDescription(char c) {
       
   340 	String desc = "";
       
   341 
       
   342 	if (TextUtil.isPrintable(c)) {
       
   343 	    desc = String.format("'%c',", c);
       
   344 	}
       
   345 
       
   346 	desc += String.format("%d,0x%02x", (int)c, (int)c);
       
   347 
       
   348 	return desc;
       
   349     }
       
   350 }