Clover coverage report - Maven Clover report
Coverage timestamp: Sun Aug 20 2006 04:01:04 PDT
file stats: LOC: 284   Methods: 17
NCLOC: 202   Classes: 2
 
 Source file Conditionals Statements Methods TOTAL
MimeMultipart.java 73.8% 83% 70.6% 79.5%
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 javax.mail.internet;
 19   
 20    import java.io.IOException;
 21    import java.io.InputStream;
 22    import java.io.OutputStream;
 23    import java.io.BufferedInputStream;
 24    import java.io.PushbackInputStream;
 25    import javax.activation.DataSource;
 26    import javax.mail.BodyPart;
 27    import javax.mail.MessagingException;
 28    import javax.mail.Multipart;
 29    import javax.mail.MultipartDataSource;
 30   
 31    /**
 32    * @version $Rev: 398618 $ $Date: 2006-05-01 08:18:57 -0700 (Mon, 01 May 2006) $
 33    */
 34    public class MimeMultipart extends Multipart {
 35    /**
 36    * DataSource that provides our InputStream.
 37    */
 38    protected DataSource ds;
 39    /**
 40    * Indicates if the data has been parsed.
 41    */
 42    protected boolean parsed = true;
 43   
 44    private transient ContentType type;
 45   
 46    /**
 47    * Create an empty MimeMultipart with content type "multipart/mixed"
 48    */
 49  2 public MimeMultipart() {
 50  2 this("mixed");
 51    }
 52   
 53    /**
 54    * Create an empty MimeMultipart with the subtype supplied.
 55    *
 56    * @param subtype the subtype
 57    */
 58  3 public MimeMultipart(String subtype) {
 59  3 type = new ContentType("multipart", subtype, null);
 60  3 type.setParameter("boundary", getBoundary());
 61  3 contentType = type.toString();
 62    }
 63   
 64    /**
 65    * Create a MimeMultipart from the supplied DataSource.
 66    *
 67    * @param dataSource the DataSource to use
 68    * @throws MessagingException
 69    */
 70  2 public MimeMultipart(DataSource dataSource) throws MessagingException {
 71  2 ds = dataSource;
 72  2 if (dataSource instanceof MultipartDataSource) {
 73  0 super.setMultipartDataSource((MultipartDataSource) dataSource);
 74  0 parsed = true;
 75    } else {
 76  2 type = new ContentType(ds.getContentType());
 77  2 contentType = type.toString();
 78  2 parsed = false;
 79    }
 80    }
 81   
 82  0 public void setSubType(String subtype) throws MessagingException {
 83  0 type.setSubType(subtype);
 84  0 contentType = type.toString();
 85    }
 86   
 87  2 public int getCount() throws MessagingException {
 88  2 parse();
 89  2 return super.getCount();
 90    }
 91   
 92  4 public synchronized BodyPart getBodyPart(int part) throws MessagingException {
 93  4 parse();
 94  4 return super.getBodyPart(part);
 95    }
 96   
 97  0 public BodyPart getBodyPart(String cid) throws MessagingException {
 98  0 parse();
 99  0 for (int i = 0; i < parts.size(); i++) {
 100  0 MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i);
 101  0 if (cid.equals(bodyPart.getContentID())) {
 102  0 return bodyPart;
 103    }
 104    }
 105  0 return null;
 106    }
 107   
 108  2 protected void updateHeaders() throws MessagingException {
 109  2 parse();
 110  2 for (int i = 0; i < parts.size(); i++) {
 111  4 MimeBodyPart bodyPart = (MimeBodyPart) parts.get(i);
 112  4 bodyPart.updateHeaders();
 113    }
 114    }
 115   
 116    private static byte[] dash = { '-', '-' };
 117    private static byte[] crlf = { 13, 10 };
 118   
 119  4 public void writeTo(OutputStream out) throws IOException, MessagingException {
 120  4 parse();
 121  4 String boundary = type.getParameter("boundary");
 122  4 byte[] bytes = boundary.getBytes();
 123  4 for (int i = 0; i < parts.size(); i++) {
 124  8 BodyPart bodyPart = (BodyPart) parts.get(i);
 125  8 out.write(dash);
 126  8 out.write(bytes);
 127  8 out.write(crlf);
 128  8 bodyPart.writeTo(out);
 129  8 out.write(crlf);
 130    }
 131  4 out.write(dash);
 132  4 out.write(bytes);
 133  4 out.write(dash);
 134  4 out.write(crlf);
 135  4 out.flush();
 136    }
 137   
 138  12 protected void parse() throws MessagingException {
 139  12 if (parsed) {
 140  11 return;
 141    }
 142  1 try {
 143  1 ContentType cType = new ContentType(contentType);
 144  1 byte[] boundary = ("--" + cType.getParameter("boundary")).getBytes();
 145  1 InputStream is = new BufferedInputStream(ds.getInputStream());
 146  1 PushbackInputStream pushbackInStream = new PushbackInputStream(is,
 147    (boundary.length + 2));
 148  1 readTillFirstBoundary(pushbackInStream, boundary);
 149  1 while (pushbackInStream.available()>0){
 150  2 MimeBodyPartInputStream partStream;
 151  2 partStream = new MimeBodyPartInputStream(pushbackInStream,
 152    boundary);
 153  2 addBodyPart(new MimeBodyPart(partStream));
 154    }
 155    } catch (Exception e){
 156  0 throw new MessagingException(e.toString(),e);
 157    }
 158  1 parsed = true;
 159    }
 160   
 161    /**
 162    * Move the read pointer to the begining of the first part
 163    * read till the end of first boundary
 164    *
 165    * @param pushbackInStream
 166    * @param boundary
 167    * @throws MessagingException
 168    */
 169  1 private boolean readTillFirstBoundary(PushbackInputStream pushbackInStream, byte[] boundary) throws MessagingException {
 170  1 try {
 171  1 while (pushbackInStream.available() > 0) {
 172  1 int value = pushbackInStream.read();
 173  1 if ((byte) value == boundary[0]) {
 174  1 int boundaryIndex = 0;
 175  1 while (pushbackInStream.available() > 0 && (boundaryIndex < boundary.length)
 176    && ((byte) value == boundary[boundaryIndex])) {
 177  37 value = pushbackInStream.read();
 178  37 if (value == -1)
 179  0 throw new MessagingException(
 180    "Unexpected End of Stream while searching for first Mime Boundary");
 181  37 boundaryIndex++;
 182    }
 183  1 if (boundaryIndex == boundary.length) { // boundary found
 184  1 pushbackInStream.read();
 185  1 return true;
 186    }
 187    }
 188    }
 189    } catch (IOException ioe) {
 190  0 throw new MessagingException(ioe.toString(), ioe);
 191    }
 192  0 return false;
 193    }
 194   
 195  0 protected InternetHeaders createInternetHeaders(InputStream in) throws MessagingException {
 196  0 return new InternetHeaders(in);
 197    }
 198   
 199  0 protected MimeBodyPart createMimeBodyPart(InternetHeaders headers, byte[] data) throws MessagingException {
 200  0 return new MimeBodyPart(headers, data);
 201    }
 202   
 203  0 protected MimeBodyPart createMimeBodyPart(InputStream in) throws MessagingException {
 204  0 return new MimeBodyPart(in);
 205    }
 206   
 207    private static int part;
 208   
 209  3 private synchronized static String getBoundary() {
 210  3 int i;
 211  3 synchronized(MimeMultipart.class) {
 212  3 i = part++;
 213    }
 214  3 StringBuffer buf = new StringBuffer(64);
 215  3 buf.append("----=_Part_").append(i).append('_').append((new Object()).hashCode()).append('.').append(System.currentTimeMillis());
 216  3 return buf.toString();
 217    }
 218   
 219    private class MimeBodyPartInputStream extends InputStream {
 220    PushbackInputStream inStream;
 221    boolean boundaryFound = false;
 222    byte[] boundary;
 223   
 224  2 public MimeBodyPartInputStream(PushbackInputStream inStream,
 225    byte[] boundary) {
 226  2 super();
 227  2 this.inStream = inStream;
 228  2 this.boundary = boundary;
 229    }
 230   
 231  199 public int read() throws IOException {
 232  199 if (boundaryFound) {
 233  2 return -1;
 234    }
 235    // read the next value from stream
 236  197 int value = inStream.read();
 237    // A problem occured because all the mime parts tends to have a /r/n at the end. Making it hard to transform them to correct DataSources.
 238    // This logic introduced to handle it
 239    //TODO look more in to this && for a better way to do this
 240  197 if (value == 13) {
 241  10 value = inStream.read();
 242  10 if (value != 10) {
 243  0 inStream.unread(value);
 244  0 return 13;
 245    } else {
 246  10 value = inStream.read();
 247  10 if ((byte) value != boundary[0]) {
 248  8 inStream.unread(value);
 249  8 inStream.unread(10);
 250  8 return 13;
 251    }
 252    }
 253  187 } else if ((byte) value != boundary[0]) {
 254  178 return value;
 255    }
 256    // read value is the first byte of the boundary. Start matching the
 257    // next characters to find a boundary
 258  11 int boundaryIndex = 0;
 259  11 while ((boundaryIndex < boundary.length)
 260    && ((byte) value == boundary[boundaryIndex])) {
 261  83 value = inStream.read();
 262  83 boundaryIndex++;
 263    }
 264  11 if (boundaryIndex == boundary.length) { // boundary found
 265  2 boundaryFound = true;
 266    // read the end of line character
 267  2 if (inStream.read() == 45 && value == 45) {
 268    //Last mime boundary should have a succeeding "--"
 269    //as we are on it, read the terminating CRLF
 270  1 inStream.read();
 271  1 inStream.read();
 272    }
 273  2 return -1;
 274    }
 275    // Boundary not found. Restoring bytes skipped.
 276    // write first skipped byte, push back the rest
 277  9 if (value != -1) { // Stream might have ended
 278  9 inStream.unread(value);
 279    }
 280  9 inStream.unread(boundary, 1, boundaryIndex - 1);
 281  9 return boundary[0];
 282    }
 283    }
 284    }