Added unit tests for AbstractEntityBuilder and BodyPartBuilder; incremental improvements in builder implementations

git-svn-id: https://svn.apache.org/repos/asf/james/mime4j/trunk@1617565 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/dom/pom.xml b/dom/pom.xml
index 9fd8879..757d642 100644
--- a/dom/pom.xml
+++ b/dom/pom.xml
@@ -51,6 +51,11 @@
             <artifactId>junit</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
             <scope>test</scope>
diff --git a/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java b/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java
index 434a47f..effac9c 100644
--- a/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java
+++ b/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java
@@ -25,8 +25,10 @@
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
+import org.apache.james.mime4j.dom.BinaryBody;
 import org.apache.james.mime4j.dom.Body;
 import org.apache.james.mime4j.dom.Message;
 import org.apache.james.mime4j.dom.Multipart;
@@ -59,10 +61,10 @@
      * @param field the field to add.
      */
     public AbstractEntityBuilder addField(Field field) {
-        List<Field> values = fieldMap.get(field.getName().toLowerCase());
+        List<Field> values = fieldMap.get(field.getName().toLowerCase(Locale.US));
         if (values == null) {
             values = new LinkedList<Field>();
-            fieldMap.put(field.getName().toLowerCase(), values);
+            fieldMap.put(field.getName().toLowerCase(Locale.US), values);
         }
         values.add(field);
         fields.add(field);
@@ -87,7 +89,7 @@
      * @return the field or <code>null</code> if none found.
      */
     public Field getField(String name) {
-        List<Field> l = fieldMap.get(name.toLowerCase());
+        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
         if (l != null && !l.isEmpty()) {
             return l.get(0);
         }
@@ -103,7 +105,7 @@
      * set field with the given name, <code>false<code/> otherwise.
      */
     public boolean containsField(String name) {
-        List<Field> l = fieldMap.get(name.toLowerCase());
+        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
         return l != null && !l.isEmpty();
     }
 
@@ -114,7 +116,7 @@
      * @return the list of fields.
      */
     public List<Field> getFields(final String name) {
-        final String lowerCaseName = name.toLowerCase();
+        final String lowerCaseName = name.toLowerCase(Locale.US);
         final List<Field> l = fieldMap.get(lowerCaseName);
         final List<Field> results;
         if (l == null || l.isEmpty()) {
@@ -132,7 +134,7 @@
      *            the field name (e.g. From, Subject).
      */
     public AbstractEntityBuilder removeFields(String name) {
-        final String lowerCaseName = name.toLowerCase();
+        final String lowerCaseName = name.toLowerCase(Locale.US);
         List<Field> removed = fieldMap.remove(lowerCaseName);
         if (removed == null || removed.isEmpty()) {
             return this;
@@ -159,7 +161,7 @@
      * @param field the field to set.
      */
     public AbstractEntityBuilder setField(Field field) {
-        final String lowerCaseName = field.getName().toLowerCase();
+        final String lowerCaseName = field.getName().toLowerCase(Locale.US);
         List<Field> l = fieldMap.get(lowerCaseName);
         if (l == null || l.isEmpty()) {
             addField(field);
@@ -434,25 +436,85 @@
      */
     public AbstractEntityBuilder setBody(Body body) {
         this.body = body;
-        if (!containsField(FieldName.CONTENT_TYPE) && body != null) {
-            if (body instanceof Message) {
-                setField(Fields.contentType("message/rfc822"));
-            } else if (body instanceof Multipart) {
-                Multipart multipart = (Multipart) body;
+        if (body == null) {
+            removeFields(FieldName.CONTENT_TYPE);
+        }
+        return this;
+    }
+
+    /**
+     * Sets body of this message.  Also sets the content type based on properties of
+     * the given {@link org.apache.james.mime4j.dom.Body}.
+     *
+     * @param body
+     *            the body.
+     */
+    public AbstractEntityBuilder setBody(TextBody textBody) {
+        this.body = textBody;
+        if (textBody != null) {
+            String mimeCharset = textBody.getMimeCharset();
+            if ("us-ascii".equalsIgnoreCase(mimeCharset)) {
+                mimeCharset = null;
+            }
+            if (mimeCharset != null) {
+                setField(Fields.contentType("text/plain", new NameValuePair("charset", mimeCharset)));
+            } else {
+                setField(Fields.contentType("text/plain"));
+            }
+        } else {
+            removeFields(FieldName.CONTENT_TYPE);
+        }
+        return this;
+    }
+
+    /**
+     * Sets binaryBody of this message.  Also sets the content type based on properties of
+     * the given {@link org.apache.james.mime4j.dom.Body}.
+     *
+     * @param binaryBody
+     *            the binaryBody.
+     */
+    public AbstractEntityBuilder setBody(BinaryBody binaryBody) {
+        this.body = binaryBody;
+        if (binaryBody != null) {
+            setField(Fields.contentType("application/octet-stream"));
+        } else {
+            removeFields(FieldName.CONTENT_TYPE);
+        }
+        return this;
+    }
+
+    /**
+     * Sets body of this message.  Also sets the content type based on properties of
+     * the given {@link org.apache.james.mime4j.dom.Body}.
+     *
+     * @param body
+     *            the body.
+     */
+    public AbstractEntityBuilder setBody(Message message) {
+        this.body = message;
+        if (message != null) {
+            setField(Fields.contentType("message/rfc822"));
+        } else {
+            removeFields(FieldName.CONTENT_TYPE);
+        }
+        return this;
+    }
+
+    /**
+     * Sets body of this message.  Also sets the content type based on properties of
+     * the given {@link org.apache.james.mime4j.dom.Body}.
+     *
+     * @param body
+     *            the body.
+     */
+    public AbstractEntityBuilder setBody(Multipart multipart) {
+        this.body = multipart;
+        if (multipart != null) {
                 setField(Fields.contentType("multipart/" + multipart.getSubType(),
                         new NameValuePair("boundary", MimeUtil.createUniqueBoundary())));
-            } else if (body instanceof TextBody) {
-                TextBody textBody = (TextBody) body;
-                String mimeCharset = textBody.getMimeCharset();
-                if ("us-ascii".equalsIgnoreCase(mimeCharset)) {
-                    mimeCharset = null;
-                }
-                if (mimeCharset != null) {
-                    setField(Fields.contentType("text/plain", new NameValuePair("charset", mimeCharset)));
-                } else {
-                    setField(Fields.contentType("text/plain"));
-                }
-            }
+        } else {
+            removeFields(FieldName.CONTENT_TYPE);
         }
         return this;
     }
diff --git a/dom/src/main/java/org/apache/james/mime4j/message/BodyPartBuilder.java b/dom/src/main/java/org/apache/james/mime4j/message/BodyPartBuilder.java
index 066efe3..2864cc1 100644
--- a/dom/src/main/java/org/apache/james/mime4j/message/BodyPartBuilder.java
+++ b/dom/src/main/java/org/apache/james/mime4j/message/BodyPartBuilder.java
@@ -25,6 +25,8 @@
 
 import org.apache.james.mime4j.dom.BinaryBody;
 import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
 import org.apache.james.mime4j.dom.TextBody;
 import org.apache.james.mime4j.field.Fields;
 import org.apache.james.mime4j.io.InputStreams;
@@ -118,6 +120,30 @@
         return this;
     }
 
+    @Override
+    public BodyPartBuilder setBody(TextBody textBody) {
+        super.setBody(textBody);
+        return this;
+    }
+
+    @Override
+    public BodyPartBuilder setBody(BinaryBody binaryBody) {
+        super.setBody(binaryBody);
+        return this;
+    }
+
+    @Override
+    public BodyPartBuilder setBody(Multipart multipart) {
+        super.setBody(multipart);
+        return this;
+    }
+
+    @Override
+    public BodyPartBuilder setBody(Message message) {
+        super.setBody(message);
+        return this;
+    }
+
     /**
      * Sets text of this message with the charset.
      *
@@ -142,14 +168,13 @@
      *            &quot;xml&quot;).
      */
     public BodyPartBuilder setBody(String text, String subtype, Charset charset) throws IOException {
-        if (subtype != null) {
-            if (charset != null) {
-                setField(Fields.contentType("text/" + subtype, new NameValuePair("charset", charset.name())));
-            } else {
-                setField(Fields.contentType("text/" + subtype));
-            }
+        String mimeType = "text/" + (subtype != null ? subtype : "plain");
+        if (charset != null) {
+            setField(Fields.contentType(mimeType, new NameValuePair("charset", charset.name())));
+        } else {
+            setField(Fields.contentType(mimeType));
         }
-        TextBody textBody;
+        Body textBody;
         if (bodyFactory != null) {
             textBody = bodyFactory.textBody(
                     InputStreams.create(text, charset),
@@ -170,10 +195,8 @@
      *            (&quot;type/subtype&quot;).
      */
     public BodyPartBuilder setBody(byte[] bin, String mimeType) throws IOException {
-        if (mimeType != null) {
-            setField(Fields.contentType(mimeType));
-        }
-        BinaryBody binBody;
+        setField(Fields.contentType(mimeType != null ? mimeType : "application/octet-stream"));
+        Body binBody;
         if (bodyFactory != null) {
             binBody = bodyFactory.binaryBody(InputStreams.create(bin));
         } else {
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 31a6c40..80e6724 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
@@ -241,6 +241,30 @@
         return this;
     }
 
+    @Override
+    public MessageBuilder setBody(TextBody textBody) {
+        super.setBody(textBody);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder setBody(BinaryBody binaryBody) {
+        super.setBody(binaryBody);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder setBody(Multipart multipart) {
+        super.setBody(multipart);
+        return this;
+    }
+
+    @Override
+    public MessageBuilder setBody(Message message) {
+        super.setBody(message);
+        return this;
+    }
+
     /**
      * Sets text of this message with the charset.
      *
@@ -265,14 +289,13 @@
      *            &quot;xml&quot;).
      */
     public MessageBuilder setBody(String text, String subtype, Charset charset) throws IOException {
-        if (subtype != null) {
-            if (charset != null) {
-                setField(Fields.contentType("text/" + subtype, new NameValuePair("charset", charset.name())));
-            } else {
-                setField(Fields.contentType("text/" + subtype));
-            }
+        String mimeType = "text/" + (subtype != null ? subtype : "plain");
+        if (charset != null) {
+            setField(Fields.contentType(mimeType, new NameValuePair("charset", charset.name())));
+        } else {
+            setField(Fields.contentType(mimeType));
         }
-        TextBody textBody;
+        Body textBody;
         if (bodyFactory != null) {
             textBody = bodyFactory.textBody(
                     InputStreams.create(text, charset),
@@ -293,10 +316,8 @@
      *            (&quot;type/subtype&quot;).
      */
     public MessageBuilder setBody(byte[] bin, String mimeType) throws IOException {
-        if (mimeType != null) {
-            setField(Fields.contentType(mimeType));
-        }
-        BinaryBody binBody;
+        setField(Fields.contentType(mimeType != null ? mimeType : "application/octet-stream"));
+        Body binBody;
         if (bodyFactory != null) {
             binBody = bodyFactory.binaryBody(InputStreams.create(bin));
         } else {
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 226ad93..34beca6 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
@@ -26,6 +26,7 @@
 import java.util.List;
 
 import org.apache.james.mime4j.Charsets;
+import org.apache.james.mime4j.dom.BinaryBody;
 import org.apache.james.mime4j.dom.Body;
 import org.apache.james.mime4j.dom.Entity;
 import org.apache.james.mime4j.dom.Header;
@@ -35,6 +36,7 @@
 import org.apache.james.mime4j.dom.TextBody;
 import org.apache.james.mime4j.io.InputStreams;
 import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.NameValuePair;
 
 /**
  * {@link org.apache.james.mime4j.dom.Multipart} builder.
@@ -219,10 +221,22 @@
         Charset cs = charset != null ? charset : Charsets.ISO_8859_1;
         TextBody body = bodyFactory != null ? bodyFactory.textBody(
                 InputStreams.create(text, cs), cs.name()) : BasicBodyFactory.INSTANCE.textBody(text, cs);
-        BodyPart bodyPart = new BodyPart();
-        bodyPart.setText(body);
-        bodyPart.setContentTransferEncoding("quoted-printable");
+        BodyPart bodyPart = BodyPartBuilder.create()
+                .setBody(body)
+                .setContentType("text/plain", new NameValuePair("charset", cs.name()))
+                .setContentTransferEncoding("quoted-printable")
+                .build();
+        return addBodyPart(bodyPart);
+    }
 
+    public MultipartBuilder addBinaryPart(byte[] bin, String mimeType) throws IOException {
+        BinaryBody body = bodyFactory != null ? bodyFactory.binaryBody(InputStreams.create(bin)) :
+                BasicBodyFactory.INSTANCE.binaryBody(bin);
+        BodyPart bodyPart = BodyPartBuilder.create()
+                .setBody(body)
+                .setContentType(mimeType)
+                .setContentTransferEncoding("base64")
+                .build();
         return addBodyPart(bodyPart);
     }
 
diff --git a/dom/src/test/java/org/apache/james/mime4j/message/AbstractEntityBuilderTest.java b/dom/src/test/java/org/apache/james/mime4j/message/AbstractEntityBuilderTest.java
new file mode 100644
index 0000000..58f36dc
--- /dev/null
+++ b/dom/src/test/java/org/apache/james/mime4j/message/AbstractEntityBuilderTest.java
@@ -0,0 +1,272 @@
+/****************************************************************
+* 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.text.SimpleDateFormat;
+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.Multipart;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
+import org.apache.james.mime4j.dom.field.ParsedField;
+import org.apache.james.mime4j.field.DefaultFieldParser;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.NameValuePair;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class AbstractEntityBuilderTest {
+
+    static class AbstractEntityBuilderImpl extends AbstractEntityBuilder {
+    }
+
+    @Test
+    public void testFieldOperations() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+        ParsedField field1 = DefaultFieldParser.parse("Content-Type: text/plain");
+        builder.addField(field1);
+
+        Assert.assertTrue(builder.containsField("Content-Type"));
+        Assert.assertTrue(builder.containsField("CONTENT-TYPE"));
+
+        Assert.assertSame(field1, builder.getField("Content-Type"));
+        Assert.assertSame(field1, builder.getField("CONTENT-TYPE"));
+
+        List<Field> fields1 = builder.getFields();
+        Assert.assertNotNull(fields1);
+        Assert.assertEquals(1, fields1.size());
+        Assert.assertSame(field1, fields1.get(0));
+
+        ParsedField field2 = DefaultFieldParser.parse("Content-Transfer-Encoding: 8bit");
+        builder.addField(field2);
+
+        List<Field> fields2 = builder.getFields("Stuff");
+        Assert.assertNotNull(fields2);
+        Assert.assertEquals(0, fields2.size());
+
+        List<Field> fields3 = builder.getFields("Content-Transfer-Encoding");
+        Assert.assertNotNull(fields3);
+        Assert.assertEquals(1, fields3.size());
+        Assert.assertSame(field2, fields3.get(0));
+
+        builder.removeFields("CONTENT-TYPE");
+
+        Assert.assertFalse(builder.containsField("Content-Type"));
+        Assert.assertFalse(builder.containsField("CONTENT-TYPE"));
+
+        List<Field> fields4 = builder.getFields();
+        Assert.assertNotNull(fields4);
+        Assert.assertEquals(1, fields4.size());
+        Assert.assertSame(field2, fields4.get(0));
+
+        ParsedField field3 = DefaultFieldParser.parse("Content-Transfer-Encoding: 7bit");
+        builder.addField(field3);
+
+        Assert.assertSame(field2, builder.getField("Content-Transfer-Encoding"));
+        List<Field> fields5 = builder.getFields("Content-Transfer-Encoding");
+        Assert.assertNotNull(fields5);
+        Assert.assertEquals(2, fields5.size());
+        Assert.assertSame(field2, fields5.get(0));
+        Assert.assertSame(field3, fields5.get(1));
+
+        builder.clearFields();
+
+        Assert.assertFalse(builder.containsField("Content-Type"));
+        Assert.assertFalse(builder.containsField("Content-Transfer-Encoding"));
+
+        List<Field> fields6 = builder.getFields();
+        Assert.assertNotNull(fields6);
+        Assert.assertEquals(0, fields6.size());
+    }
+
+    @Test
+    public void testContentType() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        builder.setContentType("text/plain");
+
+        Assert.assertEquals("text/plain", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        builder.setContentType("text/plain", new NameValuePair("charset", "ASCII"), new NameValuePair("stuff", null));
+
+        Assert.assertEquals("text/plain", builder.getMimeType());
+        Assert.assertEquals("ASCII", builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        builder.setContentType(null);
+
+        Assert.assertEquals(null, builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertFalse(builder.containsField("Content-Type"));
+    }
+
+    @Test
+    public void testContentTransferEncoding() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        builder.setContentTransferEncoding("7bit");
+
+        Assert.assertEquals("7bit", builder.getContentTransferEncoding());
+        Assert.assertTrue(builder.containsField("Content-Transfer-Encoding"));
+
+        builder.setContentTransferEncoding(null);
+
+        Assert.assertEquals(null, builder.getContentTransferEncoding());
+        Assert.assertFalse(builder.containsField("Content-Transfer-Encoding"));
+    }
+
+    @Test
+    public void testContentDisposition() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        builder.setContentDisposition("attachment");
+
+        Assert.assertEquals("attachment", builder.getDispositionType());
+        Assert.assertEquals(null, builder.getFilename());
+        Assert.assertEquals(-1L, builder.getSize());
+        Assert.assertEquals(null, builder.getCreationDate());
+        Assert.assertEquals(null, builder.getModificationDate());
+        Assert.assertEquals(null, builder.getReadDate());
+        Assert.assertTrue(builder.containsField("Content-Disposition"));
+
+        builder.setContentDisposition("attachment", "some-file.txt");
+
+        Assert.assertEquals("attachment", builder.getDispositionType());
+        Assert.assertEquals("some-file.txt", builder.getFilename());
+        Assert.assertEquals(-1L, builder.getSize());
+        Assert.assertEquals(null, builder.getCreationDate());
+        Assert.assertEquals(null, builder.getModificationDate());
+        Assert.assertEquals(null, builder.getReadDate());
+        Assert.assertTrue(builder.containsField("Content-Disposition"));
+
+        builder.setContentDisposition("attachment", "some-other-file.txt", 1234L);
+
+        Assert.assertEquals("attachment", builder.getDispositionType());
+        Assert.assertEquals("some-other-file.txt", builder.getFilename());
+        Assert.assertEquals(1234L, builder.getSize());
+        Assert.assertEquals(null, builder.getCreationDate());
+        Assert.assertEquals(null, builder.getModificationDate());
+        Assert.assertEquals(null, builder.getReadDate());
+        Assert.assertTrue(builder.containsField("Content-Disposition"));
+
+        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
+        dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+        Date t1 = dateFormat.parse("2014-08-01 12:00");
+        Date t2 = dateFormat.parse("2014-08-01 12:30");
+        Date t3 = dateFormat.parse("2014-08-01 14:00");
+        builder.setContentDisposition("attachment", "some-other-file.txt", 1234L, t1, t2, t3);
+
+        Assert.assertEquals("attachment", builder.getDispositionType());
+        Assert.assertEquals("some-other-file.txt", builder.getFilename());
+        Assert.assertEquals(1234L, builder.getSize());
+        Assert.assertEquals(t1, builder.getCreationDate());
+        Assert.assertEquals(t2, builder.getModificationDate());
+        Assert.assertEquals(t3, builder.getReadDate());
+        Assert.assertTrue(builder.containsField("Content-Disposition"));
+
+        builder.setContentDisposition(null);
+
+        Assert.assertEquals(null, builder.getDispositionType());
+        Assert.assertFalse(builder.containsField("Content-Disposition"));
+    }
+
+    @Test
+    public void testSetTextBody() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        TextBody body1 = BasicBodyFactory.INSTANCE.textBody("test", Charsets.ISO_8859_1);
+        builder.setBody(body1);
+
+        Assert.assertSame(body1, builder.getBody());
+        Assert.assertEquals("text/plain", builder.getMimeType());
+        Assert.assertEquals("ISO-8859-1", builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        TextBody body2 = BasicBodyFactory.INSTANCE.textBody("test");
+        builder.setBody(body2);
+
+        Assert.assertSame(body2, builder.getBody());
+        Assert.assertEquals("text/plain", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        builder.setBody((TextBody) null);
+        Assert.assertFalse(builder.containsField("Content-Type"));
+    }
+
+    @Test
+    public void testSetBinaryBody() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        BinaryBody body = BasicBodyFactory.INSTANCE.binaryBody(new byte[]{1, 2, 3});
+        builder.setBody(body);
+
+        Assert.assertSame(body, builder.getBody());
+        Assert.assertEquals("application/octet-stream", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        builder.setBody((TextBody) null);
+        Assert.assertFalse(builder.containsField("Content-Type"));
+    }
+
+    @Test
+    public void testSetMessageBody() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        Message message = new MessageImpl();
+        builder.setBody(message);
+
+        Assert.assertSame(message, builder.getBody());
+        Assert.assertEquals("message/rfc822", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        builder.setBody((Message) null);
+        Assert.assertFalse(builder.containsField("Content-Type"));
+    }
+
+    @Test
+    public void testSetMultipartBody() throws Exception {
+        AbstractEntityBuilder builder = new AbstractEntityBuilderImpl();
+
+        Multipart multipart = new MultipartImpl("stuff");
+        builder.setBody(multipart);
+
+        Assert.assertSame(multipart, builder.getBody());
+        Assert.assertEquals("multipart/stuff", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+        Assert.assertTrue(builder.containsField("Content-Type"));
+
+        final ContentTypeField field = (ContentTypeField) builder.getField("Content-Type");
+        Assert.assertNotNull(field.getBoundary());
+
+        builder.setBody((Message) null);
+        Assert.assertFalse(builder.containsField("Content-Type"));
+    }
+
+}
diff --git a/dom/src/test/java/org/apache/james/mime4j/message/BodyPartBuilderTest.java b/dom/src/test/java/org/apache/james/mime4j/message/BodyPartBuilderTest.java
new file mode 100644
index 0000000..7a91b2b
--- /dev/null
+++ b/dom/src/test/java/org/apache/james/mime4j/message/BodyPartBuilderTest.java
@@ -0,0 +1,109 @@
+/****************************************************************
+ * 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.List;
+
+import org.apache.james.mime4j.Charsets;
+import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.TextBody;
+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 BodyPartBuilderTest {
+
+    @Test
+    public void testSetBody() throws Exception {
+        BodyPartBuilder builder = BodyPartBuilder.create();
+
+        Body body = BasicBodyFactory.INSTANCE.textBody("test");
+        Assert.assertNull(body.getParent());
+
+        builder.setBody(body);
+        final BodyPart bodyPart = builder.build();
+        Assert.assertSame(body, bodyPart.getBody());
+        Assert.assertSame(bodyPart, body.getParent());
+    }
+
+    @Test
+    public void testSetTextBody() throws Exception {
+        BodyPartBuilder builder = BodyPartBuilder.create();
+
+        BodyFactory bodyFactory = Mockito.spy(new BasicBodyFactory());
+        builder.use(bodyFactory);
+
+        builder.setBody("blah", Charsets.ISO_8859_1);
+        Assert.assertEquals("text/plain", builder.getMimeType());
+        Assert.assertEquals("ISO-8859-1", builder.getCharset());
+
+        builder.setBody("blah", "stuff", Charsets.ISO_8859_1);
+        Assert.assertEquals("text/stuff", builder.getMimeType());
+        Assert.assertEquals("ISO-8859-1", builder.getCharset());
+
+        builder.setBody("blah", "stuff", null);
+        Assert.assertEquals("text/stuff", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+
+        BodyPart bodyPart = builder.build();
+        Assert.assertNotNull(bodyPart);
+        Body body = bodyPart.getBody();
+        Assert.assertSame(bodyPart, body.getParent());
+        ContentTypeField field = (ContentTypeField) bodyPart.getHeader().getField("Content-Type");
+        Assert.assertEquals("text/stuff", field.getMimeType());
+        Assert.assertEquals(null, field.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(2)).textBody(
+                Mockito.<InputStream>any(), Mockito.eq("ISO-8859-1"));
+        Mockito.verify(bodyFactory, Mockito.times(1)).textBody(
+                Mockito.<InputStream>any(),  Mockito.isNull(String.class));
+
+    }
+
+    @Test
+    public void testSetBinaryBody() throws Exception {
+        BodyPartBuilder builder = BodyPartBuilder.create();
+
+        BodyFactory bodyFactory = Mockito.spy(new BasicBodyFactory());
+        builder.use(bodyFactory);
+
+        builder.setBody(new byte[] {1,2,3}, "some/stuff");
+        Assert.assertEquals("some/stuff", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+
+        builder.setBody(new byte[] {1,2,3,4}, null);
+        Assert.assertEquals("application/octet-stream", builder.getMimeType());
+        Assert.assertEquals(null, builder.getCharset());
+
+        BodyPart bodyPart = builder.build();
+        Assert.assertNotNull(bodyPart);
+        Body body = bodyPart.getBody();
+        Assert.assertSame(bodyPart, body.getParent());
+        ContentTypeField field = (ContentTypeField) bodyPart.getHeader().getField("Content-Type");
+        Assert.assertEquals("application/octet-stream", field.getMimeType());
+        Assert.assertEquals(null, field.getCharset());
+
+        Mockito.verify(bodyFactory, Mockito.times(2)).binaryBody(Mockito.<InputStream>any());
+    }
+
+}
diff --git a/pom.xml b/pom.xml
index 76bc807..d763fc8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -68,6 +68,7 @@
         <commons-logging.version>1.1.1</commons-logging.version>
         <log4j.version>1.2.14</log4j.version>
         <junit.version>4.10</junit.version>
+        <mockito.version>1.9.5</mockito.version>
         <!-- Version 2.2 is required for Java 1.5 compatibility -->
         <commons-io.version>2.2</commons-io.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -123,6 +124,12 @@
                 <scope>test</scope>
             </dependency>
             <dependency>
+                <groupId>org.mockito</groupId>
+                <artifactId>mockito-core</artifactId>
+                <version>${mockito.version}</version>
+                <scope>test</scope>
+            </dependency>
+            <dependency>
                 <groupId>commons-io</groupId>
                 <artifactId>commons-io</artifactId>
                 <version>${commons-io.version}</version>