Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.util;
     18 
     19 import java.io.FilterOutputStream;
     20 import java.io.IOException;
     21 import java.io.OutputStream;
     22 
     23 /**
     24  * An OutputStream that does Base64 encoding on the data written to
     25  * it, writing the resulting data to another OutputStream.
     26  */
     27 public class Base64OutputStream extends FilterOutputStream {
     28     private final Base64.Coder coder;
     29     private final int flags;
     30 
     31     private byte[] buffer = null;
     32     private int bpos = 0;
     33 
     34     private static byte[] EMPTY = new byte[0];
     35 
     36     /**
     37      * Performs Base64 encoding on the data written to the stream,
     38      * writing the encoded data to another OutputStream.
     39      *
     40      * @param out the OutputStream to write the encoded data to
     41      * @param flags bit flags for controlling the encoder; see the
     42      *        constants in {@link Base64}
     43      */
     44     public Base64OutputStream(OutputStream out, int flags) {
     45         this(out, flags, true);
     46     }
     47 
     48     /**
     49      * Performs Base64 encoding or decoding on the data written to the
     50      * stream, writing the encoded/decoded data to another
     51      * OutputStream.
     52      *
     53      * @param out the OutputStream to write the encoded data to
     54      * @param flags bit flags for controlling the encoder; see the
     55      *        constants in {@link Base64}
     56      * @param encode true to encode, false to decode
     57      *
     58      * @hide
     59      */
     60     public Base64OutputStream(OutputStream out, int flags, boolean encode) {
     61         super(out);
     62         this.flags = flags;
     63         if (encode) {
     64             coder = new Base64.Encoder(flags, null);
     65         } else {
     66             coder = new Base64.Decoder(flags, null);
     67         }
     68     }
     69 
     70     public void write(int b) throws IOException {
     71         // To avoid invoking the encoder/decoder routines for single
     72         // bytes, we buffer up calls to write(int) in an internal
     73         // byte array to transform them into writes of decently-sized
     74         // arrays.
     75 
     76         if (buffer == null) {
     77             buffer = new byte[1024];
     78         }
     79         if (bpos >= buffer.length) {
     80             // internal buffer full; write it out.
     81             internalWrite(buffer, 0, bpos, false);
     82             bpos = 0;
     83         }
     84         buffer[bpos++] = (byte) b;
     85     }
     86 
     87     /**
     88      * Flush any buffered data from calls to write(int).  Needed
     89      * before doing a write(byte[], int, int) or a close().
     90      */
     91     private void flushBuffer() throws IOException {
     92         if (bpos > 0) {
     93             internalWrite(buffer, 0, bpos, false);
     94             bpos = 0;
     95         }
     96     }
     97 
     98     public void write(byte[] b, int off, int len) throws IOException {
     99         if (len <= 0) return;
    100         flushBuffer();
    101         internalWrite(b, off, len, false);
    102     }
    103 
    104     public void close() throws IOException {
    105         IOException thrown = null;
    106         try {
    107             flushBuffer();
    108             internalWrite(EMPTY, 0, 0, true);
    109         } catch (IOException e) {
    110             thrown = e;
    111         }
    112 
    113         try {
    114             if ((flags & Base64.NO_CLOSE) == 0) {
    115                 out.close();
    116             } else {
    117                 out.flush();
    118             }
    119         } catch (IOException e) {
    120             if (thrown != null) {
    121                 thrown = e;
    122             }
    123         }
    124 
    125         if (thrown != null) {
    126             throw thrown;
    127         }
    128     }
    129 
    130     /**
    131      * Write the given bytes to the encoder/decoder.
    132      *
    133      * @param finish true if this is the last batch of input, to cause
    134      *        encoder/decoder state to be finalized.
    135      */
    136     private void internalWrite(byte[] b, int off, int len, boolean finish) throws IOException {
    137         coder.output = embiggen(coder.output, coder.maxOutputSize(len));
    138         if (!coder.process(b, off, len, finish)) {
    139             throw new Base64DataException("bad base-64");
    140         }
    141         out.write(coder.output, 0, coder.op);
    142     }
    143 
    144     /**
    145      * If b.length is at least len, return b.  Otherwise return a new
    146      * byte array of length len.
    147      */
    148     private byte[] embiggen(byte[] b, int len) {
    149         if (b == null || b.length < len) {
    150             return new byte[len];
    151         } else {
    152             return b;
    153         }
    154     }
    155 }
    156