Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 208   Methods: 11
NCLOC: 96   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Base64DecoderStream.java 62.5% 69.6% 54.5% 65.4%
coverage coverage
 1    /**
 2    *
 3    * Copyright 2003-2004 The Apache Software Foundation
 4    *
 5    * Licensed under the Apache License, Version 2.0 (the "License");
 6    * you may not use this file except in compliance with the License.
 7    * You may obtain a copy of the License at
 8    *
 9    * http://www.apache.org/licenses/LICENSE-2.0
 10    *
 11    * Unless required by applicable law or agreed to in writing, software
 12    * distributed under the License is distributed on an "AS IS" BASIS,
 13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 14    * See the License for the specific language governing permissions and
 15    * limitations under the License.
 16    */
 17   
 18    package org.apache.geronimo.mail.util;
 19   
 20    import java.io.IOException;
 21    import java.io.InputStream;
 22    import java.io.FilterInputStream;
 23   
 24    /**
 25    * An implementation of a FilterInputStream that decodes the
 26    * stream data in BASE64 encoding format. This version does the
 27    * decoding "on the fly" rather than decoding a single block of
 28    * data. Since this version is intended for use by the MimeUtilty class,
 29    * it also handles line breaks in the encoded data.
 30    */
 31    public class Base64DecoderStream extends FilterInputStream {
 32   
 33    static protected final String MAIL_BASE64_IGNOREERRORS = "mail.mime.base64.ignoreerrors";
 34   
 35    // number of decodeable units we'll try to process at one time. We'll attempt to read that much
 36    // data from the input stream and decode in blocks.
 37    static protected final int BUFFERED_UNITS = 2000;
 38   
 39    // our decoder for processing the data
 40    protected Base64Encoder decoder = new Base64Encoder();
 41   
 42    // can be overridden by a system property.
 43    protected boolean ignoreErrors = false;
 44   
 45    // buffer for reading in chars for decoding (which can support larger bulk reads)
 46    protected byte[] encodedChars = new byte[BUFFERED_UNITS * 4];
 47    // a buffer for one decoding unit's worth of data (3 bytes). This is the minimum amount we
 48    // can read at one time.
 49    protected byte[] decodedChars = new byte[BUFFERED_UNITS * 3];
 50    // count of characters in the buffer
 51    protected int decodedCount = 0;
 52    // index of the next decoded character
 53    protected int decodedIndex = 0;
 54   
 55   
 56  4 public Base64DecoderStream(InputStream in) {
 57  4 super(in);
 58    // make sure we get the ignore errors flag
 59  4 ignoreErrors = SessionUtil.getBooleanProperty(MAIL_BASE64_IGNOREERRORS, false);
 60    }
 61   
 62    /**
 63    * Test for the existance of decoded characters in our buffer
 64    * of decoded data.
 65    *
 66    * @return True if we currently have buffered characters.
 67    */
 68  6 private boolean dataAvailable() {
 69  6 return decodedCount != 0;
 70    }
 71   
 72    /**
 73    * Get the next buffered decoded character.
 74    *
 75    * @return The next decoded character in the buffer.
 76    */
 77  0 private byte getBufferedChar() {
 78  0 decodedCount--;
 79  0 return decodedChars[decodedIndex++];
 80    }
 81   
 82    /**
 83    * Decode a requested number of bytes of data into a buffer.
 84    *
 85    * @return true if we were able to obtain more data, false otherwise.
 86    */
 87  6 private boolean decodeStreamData() throws IOException {
 88  6 decodedIndex = 0;
 89   
 90    // fill up a data buffer with input data
 91  6 int readCharacters = fillEncodedBuffer();
 92   
 93  6 if (readCharacters > 0) {
 94  4 decodedCount = decoder.decode(encodedChars, 0, readCharacters, decodedChars);
 95  4 return true;
 96    }
 97  2 return false;
 98    }
 99   
 100   
 101    /**
 102    * Retrieve a single byte from the decoded characters buffer.
 103    *
 104    * @return The decoded character or -1 if there was an EOF condition.
 105    */
 106  0 private int getByte() throws IOException {
 107  0 if (!dataAvailable()) {
 108  0 if (!decodeStreamData()) {
 109  0 return -1;
 110    }
 111    }
 112  0 decodedCount--;
 113  0 return decodedChars[decodedIndex++];
 114    }
 115   
 116  5 private int getBytes(byte[] data, int offset, int length) throws IOException {
 117   
 118  5 int readCharacters = 0;
 119  5 while (length > 0) {
 120    // need data? Try to get some
 121  6 if (!dataAvailable()) {
 122    // if we can't get this, return a count of how much we did get (which may be -1).
 123  6 if (!decodeStreamData()) {
 124  2 return readCharacters > 0 ? readCharacters : -1;
 125    }
 126    }
 127   
 128    // now copy some of the data from the decoded buffer to the target buffer
 129  4 int copyCount = Math.min(decodedCount, length);
 130  4 System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount);
 131  4 decodedIndex += copyCount;
 132  4 decodedCount -= copyCount;
 133  4 offset += copyCount;
 134  4 length -= copyCount;
 135  4 readCharacters += copyCount;
 136    }
 137  3 return readCharacters;
 138    }
 139   
 140   
 141    /**
 142    * Fill our buffer of input characters for decoding from the
 143    * stream. This will attempt read a full buffer, but will
 144    * terminate on an EOF or read error. This will filter out
 145    * non-Base64 encoding chars and will only return a valid
 146    * multiple of 4 number of bytes.
 147    *
 148    * @return The count of characters read.
 149    */
 150  6 private int fillEncodedBuffer() throws IOException
 151    {
 152  6 int readCharacters = 0;
 153   
 154  6 while (true) {
 155    // get the next character from the stream
 156  1062 int ch = in.read();
 157    // did we hit an EOF condition?
 158  1062 if (ch == -1) {
 159    // now check to see if this is normal, or potentially an error
 160    // if we didn't get characters as a multiple of 4, we may need to complain about this.
 161  6 if ((readCharacters % 4) != 0) {
 162    // the error checking can be turned off...normally it isn't
 163  0 if (!ignoreErrors) {
 164  0 throw new IOException("Base64 encoding error, data truncated");
 165    }
 166    // we're ignoring errors, so round down to a multiple and return that.
 167  0 return (readCharacters / 4) * 4;
 168    }
 169    // return the count.
 170  6 return readCharacters;
 171    }
 172    // if this character is valid in a Base64 stream, copy it to the buffer.
 173  1056 else if (decoder.isValidBase64(ch)) {
 174  1032 encodedChars[readCharacters++] = (byte)ch;
 175    // if we've filled up the buffer, time to quit.
 176  1032 if (readCharacters >= encodedChars.length) {
 177  0 return readCharacters;
 178    }
 179    }
 180   
 181    // we're filtering out whitespace and CRLF characters, so just ignore these
 182    }
 183    }
 184   
 185   
 186    // in order to function as a filter, these streams need to override the different
 187    // read() signature.
 188   
 189  0 public int read() throws IOException
 190    {
 191  0 return getByte();
 192    }
 193   
 194   
 195  5 public int read(byte [] buffer, int offset, int length) throws IOException {
 196  5 return getBytes(buffer, offset, length);
 197    }
 198   
 199   
 200  0 public boolean markSupported() {
 201  0 return false;
 202    }
 203   
 204   
 205  0 public int available() throws IOException {
 206  0 return ((in.available() / 4) * 3) + decodedCount;
 207    }
 208    }