Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 182   Methods: 9
NCLOC: 81   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Base64EncoderStream.java 68.2% 78.4% 77.8% 75%
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.OutputStream;
 22    import java.io.FilterOutputStream;
 23   
 24    /**
 25    * An implementation of a FilterOutputStream that encodes the
 26    * stream data in BASE64 encoding format. This version does the
 27    * encoding "on the fly" rather than encoding 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 Base64EncoderStream extends FilterOutputStream {
 32   
 33    // our Filtered stream writes everything out as byte data. This allows the CRLF sequence to
 34    // be written with a single call.
 35    protected static final byte[] CRLF = { '\r', '\n' };
 36   
 37    // our hex encoder utility class.
 38    protected Base64Encoder encoder = new Base64Encoder();
 39   
 40    // our default for line breaks
 41    protected static final int DEFAULT_LINEBREAK = 76;
 42   
 43    // Data can only be written out in complete units of 3 bytes encoded as 4. Therefore, we need to buffer
 44    // as many as 2 bytes to fill out an encoding unit.
 45   
 46    // the buffered byte count
 47    protected int bufferedBytes = 0;
 48   
 49    // we'll encode this part once it is filled up.
 50    protected byte[] buffer = new byte[3];
 51   
 52   
 53    // the size we process line breaks at. If this is Integer.MAX_VALUE, no line breaks are handled.
 54    protected int lineBreak;
 55   
 56    // the number of encoded characters we've written to the stream, which determines where we
 57    // insert line breaks.
 58    protected int outputCount;
 59   
 60    /**
 61    * Create a Base64 encoder stream that wraps a specifed stream
 62    * using the default line break size.
 63    *
 64    * @param out The wrapped output stream.
 65    */
 66  4 public Base64EncoderStream(OutputStream out) {
 67  4 this(out, DEFAULT_LINEBREAK);
 68    }
 69   
 70   
 71  4 public Base64EncoderStream(OutputStream out, int lineBreak) {
 72  4 super(out);
 73    // lines are processed only in multiple of 4, so round this down.
 74  4 this.lineBreak = (lineBreak / 4) * 4 ;
 75    }
 76   
 77    // in order for this to work, we need to override the 3 different signatures for write
 78   
 79  87 public void write(int ch) throws IOException {
 80    // store this in the buffer.
 81  87 buffer[bufferedBytes++] = (byte)ch;
 82    // if the buffer is filled, encode these bytes
 83  87 if (bufferedBytes == 3) {
 84    // check for room in the current line for this character
 85  28 checkEOL(4);
 86    // write these directly to the stream.
 87  28 encoder.encode(buffer, 0, 3, out);
 88  28 bufferedBytes = 0;
 89    // and update the line length checkers
 90  28 updateLineCount(4);
 91    }
 92    }
 93   
 94  0 public void write(byte [] data) throws IOException {
 95  0 write(data, 0, data.length);
 96    }
 97   
 98  4 public void write(byte [] data, int offset, int length) throws IOException {
 99    // if we have something in the buffer, we need to write enough bytes out to flush
 100    // those into the output stream AND continue on to finish off a line. Once we're done there
 101    // we can write additional data out in complete blocks.
 102  4 while ((bufferedBytes > 0 || outputCount > 0) && length > 0) {
 103  0 write(data[offset++]);
 104  0 length--;
 105    }
 106   
 107  4 if (length > 0) {
 108    // no linebreaks requested? YES!!!!!, we can just dispose of the lot with one call.
 109  4 if (lineBreak == Integer.MAX_VALUE) {
 110  0 encoder.encode(data, offset, length, out);
 111    }
 112    else {
 113    // calculate the size of a segment we can encode directly as a line.
 114  4 int segmentSize = (lineBreak / 4) * 3;
 115   
 116    // write this out a block at a time, with separators between.
 117  4 while (length > segmentSize) {
 118    // encode a segment
 119  12 encoder.encode(data, offset, segmentSize, out);
 120    // write an EOL marker
 121  12 out.write(CRLF);
 122  12 offset += segmentSize;
 123  12 length -= segmentSize;
 124    }
 125   
 126    // any remainder we write out a byte at a time to manage the groupings and
 127    // the line count appropriately.
 128  4 if (length > 0) {
 129  4 while (length > 0) {
 130  87 write(data[offset++]);
 131  87 length--;
 132    }
 133    }
 134    }
 135    }
 136    }
 137   
 138  0 public void close() throws IOException {
 139  0 flush();
 140  0 out.close();
 141    }
 142   
 143  4 public void flush() throws IOException {
 144  4 if (bufferedBytes > 0) {
 145  2 encoder.encode(buffer, 0, bufferedBytes, out);
 146  2 bufferedBytes = 0;
 147    }
 148    }
 149   
 150   
 151    /**
 152    * Check for whether we're about the reach the end of our
 153    * line limit for an update that's about to occur. If we will
 154    * overflow, then a line break is inserted.
 155    *
 156    * @param required The space required for this pending write.
 157    *
 158    * @exception IOException
 159    */
 160  28 private void checkEOL(int required) throws IOException {
 161  28 if (lineBreak != Integer.MAX_VALUE) {
 162    // if this write would exceed the line maximum, add a linebreak to the stream.
 163  28 if (outputCount + required > lineBreak) {
 164  0 out.write(CRLF);
 165  0 outputCount = 0;
 166    }
 167    }
 168    }
 169   
 170    /**
 171    * Update the counter of characters on the current working line.
 172    * This is conditional if we're not working with a line limit.
 173    *
 174    * @param added The number of characters just added.
 175    */
 176  28 private void updateLineCount(int added) {
 177  28 if (lineBreak != Integer.MAX_VALUE) {
 178  28 outputCount += added;
 179    }
 180    }
 181    }
 182