Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 275   Methods: 13
NCLOC: 130   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
UUDecoderStream.java 53.1% 75.8% 61.5% 67.6%
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.ByteArrayOutputStream;
 21    import java.io.FilterInputStream;
 22    import java.io.IOException;
 23    import java.io.InputStream;
 24    import java.io.UnsupportedEncodingException;
 25   
 26    /**
 27    * An implementation of a FilterOutputStream that decodes the
 28    * stream data in UU encoding format. This version does the
 29    * decoding "on the fly" rather than decoding a single block of
 30    * data. Since this version is intended for use by the MimeUtilty class,
 31    * it also handles line breaks in the encoded data.
 32    */
 33    public class UUDecoderStream extends FilterInputStream {
 34    // maximum number of chars that can appear in a single line
 35    protected static final int MAX_CHARS_PER_LINE = 45;
 36   
 37    // our decoder for processing the data
 38    protected UUEncoder decoder = new UUEncoder();
 39   
 40    // a buffer for one decoding unit's worth of data (45 bytes).
 41    protected byte[] decodedChars;
 42    // count of characters in the buffer
 43    protected int decodedCount = 0;
 44    // index of the next decoded character
 45    protected int decodedIndex = 0;
 46   
 47    // indicates whether we've already processed the "begin" prefix.
 48    protected boolean beginRead = false;
 49   
 50   
 51  5 public UUDecoderStream(InputStream in) {
 52  5 super(in);
 53    }
 54   
 55   
 56    /**
 57    * Test for the existance of decoded characters in our buffer
 58    * of decoded data.
 59    *
 60    * @return True if we currently have buffered characters.
 61    */
 62  30 private boolean dataAvailable() {
 63  30 return decodedCount != 0;
 64    }
 65   
 66    /**
 67    * Get the next buffered decoded character.
 68    *
 69    * @return The next decoded character in the buffer.
 70    */
 71  0 private byte getBufferedChar() {
 72  0 decodedCount--;
 73  0 return decodedChars[decodedIndex++];
 74    }
 75   
 76    /**
 77    * Decode a requested number of bytes of data into a buffer.
 78    *
 79    * @return true if we were able to obtain more data, false otherwise.
 80    */
 81  30 private boolean decodeStreamData() throws IOException {
 82  30 decodedIndex = 0;
 83   
 84    // fill up a data buffer with input data
 85  30 return fillEncodedBuffer() != -1;
 86    }
 87   
 88   
 89    /**
 90    * Retrieve a single byte from the decoded characters buffer.
 91    *
 92    * @return The decoded character or -1 if there was an EOF condition.
 93    */
 94  0 private int getByte() throws IOException {
 95  0 if (!dataAvailable()) {
 96  0 if (!decodeStreamData()) {
 97  0 return -1;
 98    }
 99    }
 100  0 decodedCount--;
 101  0 return decodedChars[decodedIndex++];
 102    }
 103   
 104  5 private int getBytes(byte[] data, int offset, int length) throws IOException {
 105   
 106  5 int readCharacters = 0;
 107  5 while (length > 0) {
 108    // need data? Try to get some
 109  30 if (!dataAvailable()) {
 110    // if we can't get this, return a count of how much we did get (which may be -1).
 111  30 if (!decodeStreamData()) {
 112  0 return readCharacters > 0 ? readCharacters : -1;
 113    }
 114    }
 115   
 116    // now copy some of the data from the decoded buffer to the target buffer
 117  30 int copyCount = Math.min(decodedCount, length);
 118  30 System.arraycopy(decodedChars, decodedIndex, data, offset, copyCount);
 119  30 decodedIndex += copyCount;
 120  30 decodedCount -= copyCount;
 121  30 offset += copyCount;
 122  30 length -= copyCount;
 123  30 readCharacters += copyCount;
 124    }
 125  5 return readCharacters;
 126    }
 127   
 128    /**
 129    * Verify that the first line of the buffer is a valid begin
 130    * marker.
 131    *
 132    * @exception IOException
 133    */
 134  30 private void checkBegin() throws IOException {
 135    // we only do this the first time we're requested to read from the stream.
 136  30 if (beginRead) {
 137  25 return;
 138    }
 139   
 140    // we might have to skip over lines to reach the marker. If we hit the EOF without finding
 141    // the begin, that's an error.
 142  5 while (true) {
 143  5 String line = readLine();
 144  5 if (line == null) {
 145  0 throw new IOException("Missing UUEncode begin command");
 146    }
 147   
 148    // is this our begin?
 149  5 if (line.regionMatches(true, 0, "begin ", 0, 6)) {
 150    // This is the droid we're looking for.....
 151  5 beginRead = true;
 152  5 return;
 153    }
 154    }
 155    }
 156   
 157   
 158    /**
 159    * Read a line of data. Returns null if there is an EOF.
 160    *
 161    * @return The next line read from the stream. Returns null if we
 162    * hit the end of the stream.
 163    * @exception IOException
 164    */
 165  35 protected String readLine() throws IOException {
 166  35 decodedIndex = 0;
 167    // get an accumulator for the data
 168  35 StringBuffer buffer = new StringBuffer();
 169   
 170    // now process a character at a time.
 171  35 int ch = in.read();
 172  1975 while (ch != -1) {
 173    // a naked new line completes the line.
 174  1975 if (ch == '\n') {
 175  35 break;
 176    }
 177    // a carriage return by itself is ignored...we're going to assume that this is followed
 178    // by a new line because we really don't have the capability of pushing this back .
 179  1940 else if (ch == '\r') {
 180    ;
 181    }
 182    else {
 183    // add this to our buffer
 184  1935 buffer.append((char)ch);
 185    }
 186  1940 ch = in.read();
 187    }
 188   
 189    // if we didn't get any data at all, return nothing
 190  35 if (ch == -1 && buffer.length() == 0) {
 191  0 return null;
 192    }
 193    // convert this into a string.
 194  35 return buffer.toString();
 195    }
 196   
 197   
 198    /**
 199    * Fill our buffer of input characters for decoding from the
 200    * stream. This will attempt read a full buffer, but will
 201    * terminate on an EOF or read error. This will filter out
 202    * non-Base64 encoding chars and will only return a valid
 203    * multiple of 4 number of bytes.
 204    *
 205    * @return The count of characters read.
 206    */
 207  30 private int fillEncodedBuffer() throws IOException
 208    {
 209  30 checkBegin();
 210    // reset the buffer position
 211  30 decodedIndex = 0;
 212   
 213  30 while (true) {
 214   
 215    // we read these as character lines. We need to be looking for the "end" marker for the
 216    // end of the data.
 217  30 String line = readLine();
 218   
 219    // this should NOT be happening....
 220  30 if (line == null) {
 221  0 throw new IOException("Missing end in UUEncoded data");
 222    }
 223   
 224    // Is this the end marker? EOF baby, EOF!
 225  30 if (line.equalsIgnoreCase("end")) {
 226    // this indicates we got nuttin' more to do.
 227  0 return -1;
 228    }
 229   
 230  30 ByteArrayOutputStream out = new ByteArrayOutputStream(MAX_CHARS_PER_LINE);
 231   
 232  30 byte [] lineBytes;
 233  30 try {
 234  30 lineBytes = line.getBytes("US-ASCII");
 235    } catch (UnsupportedEncodingException e) {
 236  0 throw new IOException("Invalid UUEncoding");
 237    }
 238   
 239    // decode this line
 240  30 decodedCount = decoder.decode(lineBytes, 0, lineBytes.length, out);
 241   
 242    // not just a zero-length line?
 243  30 if (decodedCount != 0) {
 244    // get the resulting byte array
 245  30 decodedChars = out.toByteArray();
 246  30 return decodedCount;
 247    }
 248    }
 249    }
 250   
 251   
 252    // in order to function as a filter, these streams need to override the different
 253    // read() signature.
 254   
 255  0 public int read() throws IOException
 256    {
 257  0 return getByte();
 258    }
 259   
 260   
 261  5 public int read(byte [] buffer, int offset, int length) throws IOException {
 262  5 return getBytes(buffer, offset, length);
 263    }
 264   
 265   
 266  0 public boolean markSupported() {
 267  0 return false;
 268    }
 269   
 270   
 271  0 public int available() throws IOException {
 272  0 return ((in.available() / 4) * 3) + decodedCount;
 273    }
 274    }
 275