Added unit tests for MultipartBuilder and MessageBuilder

git-svn-id: https://svn.apache.org/repos/asf/james/mime4j/trunk@1618035 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dom/src/main/java/org/apache/james/mime4j/dom/address/AddressList.java b/dom/src/main/java/org/apache/james/mime4j/dom/address/AddressList.java
index 91b7a3d..3e7ede8 100644
--- a/dom/src/main/java/org/apache/james/mime4j/dom/address/AddressList.java
+++ b/dom/src/main/java/org/apache/james/mime4j/dom/address/AddressList.java
@@ -22,6 +22,7 @@
 import java.io.Serializable;
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -42,11 +43,31 @@
      *            modified by someone else.
      */
     public AddressList(List<? extends Address> addresses, boolean dontCopy) {
-        if (addresses != null)
-            this.addresses = dontCopy ? addresses : new ArrayList<Address>(
-                    addresses);
-        else
+        if (addresses != null) {
+            this.addresses = dontCopy ? addresses : new ArrayList<Address>(addresses);
+        } else {
             this.addresses = Collections.emptyList();
+        }
+    }
+
+    /**
+     * @param addresses
+     *            A List that contains only Address objects.
+     */
+    public AddressList(List<? extends Address> addresses) {
+        this(addresses, false);
+    }
+
+    /**
+     * @param addresses
+     *            A List that contains only Address objects.
+     */
+    public AddressList(Address... addresses) {
+        if (addresses != null) {
+            this.addresses = Arrays.asList(addresses);
+        } else {
+            this.addresses = Collections.emptyList();
+        }
     }
 
     /**
diff --git a/dom/src/main/java/org/apache/james/mime4j/dom/address/DomainList.java b/dom/src/main/java/org/apache/james/mime4j/dom/address/DomainList.java
index 161263e..e65cd13 100644
--- a/dom/src/main/java/org/apache/james/mime4j/dom/address/DomainList.java
+++ b/dom/src/main/java/org/apache/james/mime4j/dom/address/DomainList.java
@@ -22,6 +22,7 @@
 import java.io.Serializable;
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -36,13 +37,40 @@
     private final List<String> domains;
 
     /**
-     * @param domains  A List that contains only String objects.
+     * @param domains
+     *            A List that contains only String objects.
+     * @param dontCopy
+     *            true iff it is not possible for the addresses list to be
+     *            modified by someone else.
      */
-    public DomainList(List<String> domains) {
-        this.domains = (domains == null) ? Collections.<String>emptyList():new ArrayList<String>(domains);
+    public DomainList(List<String> domains, boolean dontCopy) {
+        if (domains != null) {
+            this.domains = dontCopy ? domains : new ArrayList<String>(domains);
+        } else {
+            this.domains = Collections.emptyList();
+        }
     }
 
     /**
+     * @param domains
+     *            A List that contains only String objects.
+     */
+    public DomainList(List<String> domains) {
+        this(domains, false);
+    }
+
+    /**
+     * @param domains
+     *            A List that contains only String objects.
+     */
+    public DomainList(String... domains) {
+        if (domains != null) {
+            this.domains = Arrays.asList(domains);
+        } else {
+            this.domains = Collections.emptyList();
+        }
+    }
+    /**
      * The number of elements in this list.
      */
     @Override
diff --git a/dom/src/main/java/org/apache/james/mime4j/dom/address/MailboxList.java b/dom/src/main/java/org/apache/james/mime4j/dom/address/MailboxList.java
index da1b786..db97406 100644
--- a/dom/src/main/java/org/apache/james/mime4j/dom/address/MailboxList.java
+++ b/dom/src/main/java/org/apache/james/mime4j/dom/address/MailboxList.java
@@ -22,6 +22,7 @@
 import java.io.Serializable;
 import java.util.AbstractList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
@@ -42,11 +43,31 @@
      *            modified by someone else.
      */
     public MailboxList(List<Mailbox> mailboxes, boolean dontCopy) {
-        if (mailboxes != null)
-            this.mailboxes = dontCopy ? mailboxes : new ArrayList<Mailbox>(
-                    mailboxes);
-        else
+        if (mailboxes != null) {
+            this.mailboxes = dontCopy ? mailboxes : new ArrayList<Mailbox>(mailboxes);
+        } else {
             this.mailboxes = Collections.emptyList();
+        }
+    }
+
+    /**
+     * @param mailboxes
+     *            A List that contains only Mailbox objects.
+     */
+    public MailboxList(List<Mailbox> mailboxes) {
+        this(mailboxes, false);
+    }
+
+    /**
+     * @param mailboxes
+     *            A List that contains only Mailbox objects.
+     */
+    public MailboxList(Mailbox... mailboxes) {
+        if (mailboxes != null) {
+            this.mailboxes = Arrays.asList(mailboxes);
+        } else {
+            this.mailboxes = Collections.emptyList();
+        }
     }
 
     /**
diff --git a/dom/src/main/java/org/apache/james/mime4j/field/Fields.java b/dom/src/main/java/org/apache/james/mime4j/field/Fields.java
index 6465faa..95cf83f 100644
--- a/dom/src/main/java/org/apache/james/mime4j/field/Fields.java
+++ b/dom/src/main/java/org/apache/james/mime4j/field/Fields.java
@@ -124,9 +124,9 @@
      */
     public static ContentTypeField contentType(String mimeType,
                                                Iterable<NameValuePair> parameters) {
-        if (!isValidMimeType(mimeType))
-            throw new IllegalArgumentException();
-
+        if (!isValidMimeType(mimeType)) {
+            throw new IllegalArgumentException(mimeType + " is not a valid MIME type");
+        }
         if (parameters == null) {
             return parse(ContentTypeFieldImpl.PARSER, FieldName.CONTENT_TYPE,
                     mimeType);
diff --git a/dom/src/main/java/org/apache/james/mime4j/message/MessageBuilder.java b/dom/src/main/java/org/apache/james/mime4j/message/MessageBuilder.java
index 80e6724..b51f11f 100644
--- a/dom/src/main/java/org/apache/james/mime4j/message/MessageBuilder.java
+++ b/dom/src/main/java/org/apache/james/mime4j/message/MessageBuilder.java
@@ -181,19 +181,19 @@
     }
 
     @Override
-    public AbstractEntityBuilder addField(Field field) {
+    public MessageBuilder addField(Field field) {
         super.addField(field);
         return this;
     }
 
     @Override
-    public AbstractEntityBuilder removeFields(String name) {
+    public MessageBuilder removeFields(String name) {
         super.removeFields(name);
         return this;
     }
 
     @Override
-    public AbstractEntityBuilder clearFields() {
+    public MessageBuilder clearFields() {
         super.clearFields();
         return this;
     }
@@ -834,7 +834,7 @@
     }
 
     private MessageBuilder setAddressList(String fieldName, String address) throws ParseException {
-        return setAddressList(fieldName, address == null ? null :AddressBuilder.DEFAULT.parseMailbox(address));
+        return setAddressList(fieldName, address == null ? null : AddressBuilder.DEFAULT.parseMailbox(address));
     }
 
     private MessageBuilder setAddressList(String fieldName, Address... addresses) {
diff --git a/dom/src/main/java/org/apache/james/mime4j/message/MultipartBuilder.java b/dom/src/main/java/org/apache/james/mime4j/message/MultipartBuilder.java
index 34beca6..6c6190a 100644
--- a/dom/src/main/java/org/apache/james/mime4j/message/MultipartBuilder.java
+++ b/dom/src/main/java/org/apache/james/mime4j/message/MultipartBuilder.java
@@ -224,7 +224,7 @@
         BodyPart bodyPart = BodyPartBuilder.create()
                 .setBody(body)
                 .setContentType("text/plain", new NameValuePair("charset", cs.name()))
-                .setContentTransferEncoding("quoted-printable")
+                .setContentTransferEncoding(Charsets.US_ASCII.equals(cs) ? "7bit" : "quoted-printable")
                 .build();
         return addBodyPart(bodyPart);
     }
@@ -234,7 +234,7 @@
                 BasicBodyFactory.INSTANCE.binaryBody(bin);
         BodyPart bodyPart = BodyPartBuilder.create()
                 .setBody(body)
-                .setContentType(mimeType)
+                .setContentType(mimeType != null ? mimeType : "application/octet-stream")
                 .setContentTransferEncoding("base64")
                 .build();
         return addBodyPart(bodyPart);
diff --git a/dom/src/test/java/org/apache/james/mime4j/field/address/AddressTest.java b/dom/src/test/java/org/apache/james/mime4j/field/address/AddressTest.java
index 46be95e..29b5e8e 100644
--- a/dom/src/test/java/org/apache/james/mime4j/field/address/AddressTest.java
+++ b/dom/src/test/java/org/apache/james/mime4j/field/address/AddressTest.java
@@ -59,7 +59,7 @@
 
     @Test
     public void testEmptyDomainList() {
-        DomainList dl = new DomainList(null);
+        DomainList dl = new DomainList(null, false);
         Assert.assertEquals(0, dl.size());
 
         try {
diff --git a/dom/src/test/java/org/apache/james/mime4j/message/MessageBuilderTest.java b/dom/src/test/java/org/apache/james/mime4j/message/MessageBuilderTest.java
new file mode 100644
index 0000000..35533f8
--- /dev/null
+++ b/dom/src/test/java/org/apache/james/mime4j/message/MessageBuilderTest.java
@@ -0,0 +1,438 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mime4j.message;
+
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.james.mime4j.Charsets;
+import org.apache.james.mime4j.dom.BinaryBody;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.dom.address.AddressList;
+import org.apache.james.mime4j.dom.address.Group;
+import org.apache.james.mime4j.dom.address.Mailbox;
+import org.apache.james.mime4j.dom.address.MailboxList;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
+import org.apache.james.mime4j.dom.field.FieldName;
+import org.apache.james.mime4j.field.DefaultFieldParser;
+import org.apache.james.mime4j.field.Fields;
+import org.apache.james.mime4j.field.address.AddressBuilder;
+import org.apache.james.mime4j.stream.Field;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class MessageBuilderTest {
+
+    @Test
+    public void testSetCustomBody() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        BodyFactory bodyFactory = Mockito.spy(new BasicBodyFactory());
+        builder.use(bodyFactory);
+
+        builder.setBody("stuff", "stuff", Charsets.UTF_8);
+
+        Assert.assertTrue(builder.getBody() instanceof TextBody);
+        ContentTypeField ct1 = builder.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/stuff", ct1.getMimeType());
+        Assert.assertEquals("UTF-8", ct1.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(Mockito.<InputStream>any(), Mockito.eq("UTF-8"));
+
+        builder.setBody("stuff", "other-stuff", Charsets.US_ASCII);
+
+        Assert.assertTrue(builder.getBody() instanceof TextBody);
+        ContentTypeField ct2 = builder.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/other-stuff", ct2.getMimeType());
+        Assert.assertEquals("US-ASCII", ct2.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(Mockito.<InputStream>any(), Mockito.eq("US-ASCII"));
+
+        builder.setBody("stuff", null);
+
+        Assert.assertTrue(builder.getBody() instanceof TextBody);
+        ContentTypeField ct3 = builder.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/plain", ct3.getMimeType());
+        Assert.assertEquals(null, ct3.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(Mockito.<InputStream>any(), Mockito.isNull(String.class));
+
+        builder.setBody(new byte[] {1,2,3}, null);
+
+        Assert.assertTrue(builder.getBody() instanceof BinaryBody);
+        ContentTypeField ct4 = builder.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("application/octet-stream", ct4.getMimeType());
+        Assert.assertEquals(null, ct4.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(1)).binaryBody(Mockito.<InputStream>any());
+    }
+
+    @Test
+    public void testSetMessageId() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        String id = "<msg17@localhost>";
+        builder.setMessageId(id);
+        Assert.assertEquals(id, builder.getMessageId());
+    }
+
+    @Test
+    public void testCreateMessageId() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        builder.generateMessageId("hostname");
+
+        String id = builder.getMessageId();
+        Assert.assertNotNull(id);
+        Assert.assertTrue(id.startsWith("<Mime4j."));
+        Assert.assertTrue(id.endsWith("@hostname>"));
+    }
+
+    @Test
+    public void testGetSubject() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getSubject());
+
+        String subject = "testing 1 2";
+        builder.setSubject(subject);
+        Assert.assertEquals(subject, builder.getSubject());
+
+        builder.setField(DefaultFieldParser.parse("Subject: =?windows-1252?Q?99_=80?="));
+        Assert.assertEquals("99 \u20ac", builder.getSubject());
+    }
+
+    @Test
+    public void testSetSubject() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        builder.setSubject("Semmelbr\366sel");
+        Assert.assertEquals("Semmelbr\366sel", builder.getSubject());
+        Assert.assertEquals("=?ISO-8859-1?Q?Semmelbr=F6sel?=", builder.getField(
+                "Subject").getBody());
+
+        builder.setSubject(null);
+        Assert.assertNull(builder.getField("Subject"));
+    }
+
+    @Test
+    public void testGetDate() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getDate());
+
+        builder.setField(DefaultFieldParser.parse("Date: Thu, 1 Jan 1970 05:30:00 +0530"));
+
+        Assert.assertEquals(new Date(0), builder.getDate());
+    }
+
+    @Test
+    public void testSetDate() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        builder.setDate(new Date(86400000), TimeZone.getTimeZone("GMT"));
+        Assert.assertEquals(new Date(86400000), builder.getDate());
+        Assert.assertEquals("Fri, 2 Jan 1970 00:00:00 +0000", builder.getField(
+                "Date").getBody());
+
+        builder.setDate(null);
+        Assert.assertNull(builder.getField("Date"));
+    }
+
+    @Test
+    public void testGetSender() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getSender());
+
+        builder.setField(DefaultFieldParser.parse("Sender: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", builder.getSender().getAddress());
+    }
+
+    @Test
+    public void testSetSender() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        builder.setSender("john.doe@example.net");
+        Assert.assertEquals("john.doe@example.net", builder.getField("Sender").getBody());
+
+        builder.setSender((String) null);
+        Assert.assertNull(builder.getField("Sender"));
+    }
+
+    @Test
+    public void testGetFrom() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getFrom());
+
+        builder.setField(DefaultFieldParser.parse("From: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", builder.getFrom().get(0).getAddress());
+    }
+
+    @Test
+    public void testSetFrom() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        Mailbox mailbox1 = AddressBuilder.DEFAULT.parseMailbox("john.doe@example.net");
+        Mailbox mailbox2 = AddressBuilder.DEFAULT.parseMailbox("jane.doe@example.net");
+
+        builder.setFrom(mailbox1);
+        Assert.assertEquals("john.doe@example.net", builder.getField("From")
+                .getBody());
+
+        builder.setFrom(mailbox1, mailbox2);
+        Assert.assertEquals("john.doe@example.net, jane.doe@example.net",
+                builder.getField("From").getBody());
+
+        builder.setFrom(Arrays.asList(mailbox1, mailbox2));
+        Assert.assertEquals("john.doe@example.net, jane.doe@example.net",
+                builder.getField("From").getBody());
+
+        builder.setFrom((Mailbox) null);
+        Assert.assertNull(builder.getField("From"));
+    }
+
+    @Test
+    public void testGetTo() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getTo());
+
+        builder.setField(DefaultFieldParser.parse("To: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", ((Mailbox) builder.getTo().get(0))
+                .getAddress());
+    }
+
+    @Test
+    public void testSetTo() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        Mailbox mailbox1 = AddressBuilder.DEFAULT.parseMailbox("john.doe@example.net");
+        Mailbox mailbox2 = AddressBuilder.DEFAULT.parseMailbox("jane.doe@example.net");
+        Group group = new Group("Does", mailbox1, mailbox2);
+        Mailbox mailbox3 = AddressBuilder.DEFAULT.parseMailbox("Mary Smith <mary@example.net>");
+
+        builder.setTo(group);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;",
+                builder.getField("To").getBody());
+
+        builder.setTo(group, mailbox3);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField("To")
+                .getBody());
+
+        builder.setTo(Arrays.asList(group, mailbox3));
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField("To")
+                .getBody());
+
+        builder.setTo((Mailbox) null);
+        Assert.assertNull(builder.getField("To"));
+    }
+
+    @Test
+    public void testGetCc() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getCc());
+
+        builder.setField(DefaultFieldParser.parse("Cc: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", ((Mailbox) builder.getCc().get(0))
+                .getAddress());
+    }
+
+    @Test
+    public void testSetCc() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        Mailbox mailbox1 = AddressBuilder.DEFAULT.parseMailbox("john.doe@example.net");
+        Mailbox mailbox2 = AddressBuilder.DEFAULT.parseMailbox("jane.doe@example.net");
+        Group group = new Group("Does", mailbox1, mailbox2);
+        Mailbox mailbox3 = AddressBuilder.DEFAULT.parseMailbox("Mary Smith <mary@example.net>");
+
+        builder.setCc(group);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;",
+                builder.getField("Cc").getBody());
+
+        builder.setCc(group, mailbox3);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField("Cc")
+                .getBody());
+
+        builder.setCc(Arrays.asList(group, mailbox3));
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField("Cc")
+                .getBody());
+
+        builder.setCc((Mailbox) null);
+        Assert.assertNull(builder.getField("Cc"));
+    }
+
+    @Test
+    public void testGetBcc() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getBcc());
+
+        builder.setField(DefaultFieldParser.parse("Bcc: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", ((Mailbox) builder.getBcc().get(0))
+                .getAddress());
+    }
+
+    @Test
+    public void testSetBcc() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        Mailbox mailbox1 = AddressBuilder.DEFAULT.parseMailbox("john.doe@example.net");
+        Mailbox mailbox2 = AddressBuilder.DEFAULT.parseMailbox("jane.doe@example.net");
+        Group group = new Group("Does", mailbox1, mailbox2);
+        Mailbox mailbox3 = AddressBuilder.DEFAULT.parseMailbox("Mary Smith <mary@example.net>");
+
+        builder.setBcc(group);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;",
+                builder.getField("Bcc").getBody());
+
+        builder.setBcc(group, mailbox3);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder
+                .getField("Bcc").getBody());
+
+        builder.setBcc(Arrays.asList(group, mailbox3));
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder
+                .getField("Bcc").getBody());
+
+        builder.setBcc((Mailbox) null);
+        Assert.assertNull(builder.getField("Bcc"));
+    }
+
+    @Test
+    public void testGetReplyTo() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+        Assert.assertNull(builder.getReplyTo());
+
+        builder.setField(DefaultFieldParser.parse("Reply-To: john.doe@example.net"));
+
+        Assert.assertEquals("john.doe@example.net", ((Mailbox) builder.getReplyTo().get(0))
+                .getAddress());
+    }
+
+    @Test
+    public void testSetReplyTo() throws Exception {
+        MessageBuilder builder = MessageBuilder.create();
+
+        Mailbox mailbox1 = AddressBuilder.DEFAULT.parseMailbox("john.doe@example.net");
+        Mailbox mailbox2 = AddressBuilder.DEFAULT.parseMailbox("jane.doe@example.net");
+        Group group = new Group("Does", mailbox1, mailbox2);
+        Mailbox mailbox3 = AddressBuilder.DEFAULT.parseMailbox("Mary Smith <mary@example.net>");
+
+        builder.setReplyTo(group);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;",
+                builder.getField("Reply-To").getBody());
+
+        builder.setReplyTo(group, mailbox3);
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField(
+                "Reply-To").getBody());
+
+        builder.setReplyTo(Arrays.asList(group, mailbox3));
+        Assert.assertEquals("Does: john.doe@example.net, jane.doe@example.net;, "
+                + "Mary Smith <mary@example.net>", builder.getField(
+                "Reply-To").getBody());
+
+        builder.setReplyTo((Mailbox) null);
+        Assert.assertNull(builder.getField("Reply-To"));
+    }
+
+    @Test
+    public void testBuildWithDefaults() throws Exception {
+        Message message = MessageBuilder.create()
+                .generateMessageId("hostname")
+                .setSubject("testing ...")
+                .setFrom("batman@localhost", "superman@localhost")
+                .setTo("\"Big momma\" <big_momma@localhost>")
+                .setBody("Yo, big momma!", Charsets.UTF_8)
+                .build();
+
+        Assert.assertEquals("text/plain", message.getMimeType());
+        Assert.assertNotNull(message.getMessageId());
+        Assert.assertNotNull(message.getDate());
+        Assert.assertEquals("1.0", message.getHeader().getField(FieldName.MIME_VERSION).getBody());
+        Assert.assertEquals("testing ...", message.getSubject());
+        Assert.assertEquals(new MailboxList(
+                new Mailbox("", "batman", "localhost"),
+                new Mailbox("", "superman", "localhost")), message.getFrom());
+        Assert.assertEquals(new AddressList(
+                new Mailbox("Big momma", "big_momma", "localhost")), message.getTo());
+        Assert.assertEquals("text/plain", message.getMimeType());
+        Assert.assertEquals("UTF-8", message.getCharset());
+    }
+
+    @Test
+    public void testBuildWithNoDefaults() throws Exception {
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+        Date date = dateFormat.parse("2014-08-01 12:00");
+
+        Message message = MessageBuilder.create()
+                .setMessageId("blah@hostname")
+                .setDate(date)
+                .addField(Fields.version("1.0.1"))
+                .setSubject("testing ...")
+                .setFrom("batman@localhost", "superman@localhost")
+                .setTo("\"Big momma\" <big_momma@localhost>")
+                .setBody("Yo, big momma!", Charsets.UTF_8)
+                .build();
+
+        Assert.assertEquals("text/plain", message.getMimeType());
+        Assert.assertEquals("blah@hostname", message.getMessageId());
+        Assert.assertEquals(date, message.getDate());
+        Assert.assertEquals("1.0.1", message.getHeader().getField(FieldName.MIME_VERSION).getBody());
+        Assert.assertEquals("testing ...", message.getSubject());
+        Assert.assertEquals(new MailboxList(
+                new Mailbox("", "batman", "localhost"),
+                new Mailbox("", "superman", "localhost")), message.getFrom());
+        Assert.assertEquals(new AddressList(
+                new Mailbox("Big momma", "big_momma", "localhost")), message.getTo());
+        Assert.assertEquals("text/plain", message.getMimeType());
+        Assert.assertEquals("UTF-8", message.getCharset());
+    }
+
+    @Test
+    public void testCopy() throws Exception {
+        Message original = MessageBuilder.create()
+                .generateMessageId("hostname")
+                .setSubject("testing ...")
+                .setFrom("batman@localhost", "superman@localhost")
+                .setTo("\"Big momma\" <big_momma@localhost>")
+                .setBody("Yo, big momma!", Charsets.UTF_8)
+                .build();
+
+        MessageBuilder builder = MessageBuilder.createCopy(original);
+        Assert.assertEquals(original.getHeader().getFields(), builder.getFields());
+        Assert.assertNotSame(original.getBody(), builder.getBody());
+        Assert.assertSame(original.getBody().getClass(), builder.getBody().getClass());
+    }
+
+}
diff --git a/dom/src/test/java/org/apache/james/mime4j/message/MultipartBuilderTest.java b/dom/src/test/java/org/apache/james/mime4j/message/MultipartBuilderTest.java
new file mode 100644
index 0000000..3579fb8
--- /dev/null
+++ b/dom/src/test/java/org/apache/james/mime4j/message/MultipartBuilderTest.java
@@ -0,0 +1,195 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mime4j.message;
+
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.james.mime4j.Charsets;
+import org.apache.james.mime4j.dom.BinaryBody;
+import org.apache.james.mime4j.dom.Entity;
+import org.apache.james.mime4j.dom.Header;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
+import org.apache.james.mime4j.stream.Field;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class MultipartBuilderTest {
+
+    @Test
+    public void testBasis() throws Exception {
+        MultipartBuilder builder = MultipartBuilder.create("stuff");
+        builder.setPreamble("Hello");
+        builder.setEpilogue("Bye");
+
+        Assert.assertEquals("stuff", builder.getSubType());
+        Assert.assertEquals("Hello", builder.getPreamble());
+        Assert.assertEquals("Bye", builder.getEpilogue());
+
+        TextBody body1 = BasicBodyFactory.INSTANCE.textBody("test");
+        final BodyPart bodyPart1 = new BodyPart();
+        bodyPart1.setBody(body1);
+        BinaryBody body2 = BasicBodyFactory.INSTANCE.binaryBody(new byte[]{1, 2, 3});
+        final BodyPart bodyPart2 = new BodyPart();
+        bodyPart2.setBody(body2);
+
+        builder.addBodyPart(bodyPart1);
+        builder.addBodyPart(bodyPart2, 0);
+
+        Assert.assertEquals(2, builder.getCount());
+        Assert.assertEquals(Arrays.asList(bodyPart2, bodyPart1), builder.getBodyParts());
+
+        TextBody body3 = BasicBodyFactory.INSTANCE.textBody("test");
+        final BodyPart bodyPart3 = new BodyPart();
+        bodyPart3.setBody(body3);
+
+        try {
+            builder.addBodyPart(bodyPart3, 5);
+            Assert.fail("IndexOutOfBoundsException expected");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        try {
+            builder.replaceBodyPart(bodyPart3, 5);
+            Assert.fail("IndexOutOfBoundsException expected");
+        } catch (IndexOutOfBoundsException expected) {
+        }
+        builder.replaceBodyPart(bodyPart3, 1);
+        builder.removeBodyPart(0);
+
+        Assert.assertEquals(1, builder.getCount());
+        Assert.assertEquals(Arrays.asList(bodyPart3), builder.getBodyParts());
+    }
+
+    @Test
+    public void testAddCustomPart() throws Exception {
+        MultipartBuilder builder = MultipartBuilder.create();
+
+        BodyFactory bodyFactory = Mockito.spy(new BasicBodyFactory());
+        builder.use(bodyFactory);
+
+        builder.addTextPart("stuff1", Charsets.UTF_8);
+        builder.addTextPart("stuff2", null);
+        builder.addBinaryPart(new byte[]{1, 2, 3}, "some/stuff");
+        builder.addBinaryPart(new byte[] {1,2,3}, null);
+        builder.addTextPart("stuff3", Charsets.US_ASCII);
+
+        Assert.assertEquals(5, builder.getCount());
+        List<Entity> bodyParts = builder.getBodyParts();
+        Assert.assertNotNull(bodyParts);
+        Assert.assertEquals(5, bodyParts.size());
+
+        Entity entity1 = bodyParts.get(0);
+        ContentTypeField ct1 = entity1.getHeader().getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/plain", ct1.getMimeType());
+        Assert.assertEquals("UTF-8", ct1.getCharset());
+        ContentTransferEncodingField te1 = entity1.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("quoted-printable", te1.getEncoding());
+
+        Entity entity2 = bodyParts.get(1);
+        ContentTypeField ct2 = entity2.getHeader().getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/plain", ct2.getMimeType());
+        Assert.assertEquals("ISO-8859-1", ct2.getCharset());
+        ContentTransferEncodingField te2 = entity2.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("quoted-printable", te2.getEncoding());
+
+        Entity entity3 = bodyParts.get(2);
+        ContentTypeField ct3 = entity3.getHeader().getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("some/stuff", ct3.getMimeType());
+        Assert.assertEquals(null, ct3.getCharset());
+        ContentTransferEncodingField te3 = entity3.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("base64", te3.getEncoding());
+
+        Entity entity4 = bodyParts.get(3);
+        ContentTypeField ct4 = entity4.getHeader().getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("application/octet-stream", ct4.getMimeType());
+        Assert.assertEquals(null, ct4.getCharset());
+        ContentTransferEncodingField te4 = entity4.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("base64", te4.getEncoding());
+
+        Entity entity5 = bodyParts.get(4);
+        ContentTypeField ct5 = entity5.getHeader().getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/plain", ct5.getMimeType());
+        Assert.assertEquals("US-ASCII", ct5.getCharset());
+        ContentTransferEncodingField te5 = entity5.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("7bit", te5.getEncoding());
+
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(
+                Mockito.<InputStream>any(), Mockito.eq("UTF-8"));
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(
+                Mockito.<InputStream>any(), Mockito.eq("ISO-8859-1"));
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(
+                Mockito.<InputStream>any(), Mockito.eq("US-ASCII"));
+        Mockito.verify(bodyFactory, Mockito.times(2)).binaryBody(Mockito.<InputStream>any());
+    }
+
+    @Test
+    public void testCopy() throws Exception {
+        MultipartBuilder builder1 = MultipartBuilder.create("stuff");
+        builder1.setPreamble("Hello");
+        builder1.setEpilogue("Bye");
+        builder1.addTextPart("stuff1", Charsets.UTF_8);
+        builder1.addBinaryPart(new byte[]{1, 2, 3}, "some/stuff");
+
+        Multipart multipart = builder1.build();
+
+        MultipartBuilder builder2 = MultipartBuilder.createCopy(multipart);
+        Assert.assertEquals("stuff", builder2.getSubType());
+        Assert.assertEquals("Hello", builder2.getPreamble());
+        Assert.assertEquals("Bye", builder2.getEpilogue());
+        Assert.assertEquals(2, builder2.getCount());
+
+        List<Entity> bodyParts = builder2.getBodyParts();
+
+        Assert.assertNotNull(bodyParts);
+        Assert.assertEquals(2, bodyParts.size());
+
+        Entity entity1 = bodyParts.get(0);
+        Assert.assertNotNull(entity1);
+        Header header1 = entity1.getHeader();
+        Assert.assertNotNull(header1);
+        List<Field> fields1 = header1.getFields();
+        Assert.assertNotNull(fields1);
+        Assert.assertEquals(2, fields1.size());
+        ContentTypeField ct1 = header1.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("text/plain", ct1.getMimeType());
+        Assert.assertEquals("UTF-8", ct1.getCharset());
+        ContentTransferEncodingField te1 = header1.getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("quoted-printable", te1.getEncoding());
+
+        Entity entity2 = bodyParts.get(1);
+        Assert.assertNotNull(entity2);
+        Header header2 = entity2.getHeader();
+        Assert.assertNotNull(header2);
+        List<Field> fields2 = header2.getFields();
+        Assert.assertNotNull(fields2);
+        Assert.assertEquals(2, fields2.size());
+        ContentTypeField ct2 = header2.getField("Content-Type", ContentTypeField.class);
+        Assert.assertEquals("some/stuff", ct2.getMimeType());
+        Assert.assertEquals(null, ct2.getCharset());
+        ContentTransferEncodingField te2 = entity2.getHeader().getField("Content-Transfer-Encoding", ContentTransferEncodingField.class);
+        Assert.assertEquals("base64", te2.getEncoding());
+    }
+
+}