MIME4J-262 deprecated all MessageBuilder and replace it by Message.Builder

Project: http://git-wip-us.apache.org/repos/asf/james-mime4j/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-mime4j/commit/5c583037
Tree: http://git-wip-us.apache.org/repos/asf/james-mime4j/tree/5c583037
Diff: http://git-wip-us.apache.org/repos/asf/james-mime4j/diff/5c583037

Branch: refs/heads/master
Commit: 5c5830375afc12b71055a73ce76e5895f0ab846e
Parents: c14019c
Author: Luc DUZAN <[email protected]>
Authored: Wed May 24 15:09:21 2017 +0200
Committer: benwa <[email protected]>
Committed: Thu May 25 10:11:54 2017 +0700

----------------------------------------------------------------------
 RELEASE_NOTES.txt                               |   20 +-
 .../org/apache/james/mime4j/dom/Message.java    |  913 ++++++++++++++++
 .../mime4j/internal/AbstractEntityBuilder.java  |  576 ++++++++++
 .../internal/ParserStreamContentHandler.java    |  188 ++++
 .../mime4j/message/AbstractEntityBuilder.java   |  576 ----------
 .../james/mime4j/message/BodyPartBuilder.java   |    1 +
 .../mime4j/message/DefaultMessageBuilder.java   |    1 +
 .../james/mime4j/message/MessageBuilder.java    | 1006 +++++-------------
 .../message/ParserStreamContentHandler.java     |  182 ----
 .../apache/james/mime4j/dom/MessageTest.java    |  435 ++++++++
 .../message/AbstractEntityBuilderTest.java      |    1 +
 .../mime4j/message/MessageBuilderTest.java      |  155 +--
 12 files changed, 2409 insertions(+), 1645 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/RELEASE_NOTES.txt
----------------------------------------------------------------------
diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt
index 165e541..c99eddb 100644
--- a/RELEASE_NOTES.txt
+++ b/RELEASE_NOTES.txt
@@ -1,21 +1,11 @@
 Release 0.9.0
 -------------------
 
-Fix MIME4J-262 without breaking compatibility with old code by deprecating:
- * MessageBuilder#MessageBuilder()
- * MessageBuilder#create()
- * MessageBuilder#createCopy(Message)
- * MessageBuilder#read(InputStream)
-
-and adding:
- * MessageBuilder#of()
- * MessageBuilder#of(Message)
- * MessageBuilder#of(InputStream)
-
-MessageBuilder constructed by of methods produce message for which the getDate 
method return null if and only if the
-message do not have a "Date" header. Whereas MessageBuilder constructed by 
deprecated method produce message for which
-the getDate method never return null: if the message has no "Date" header the 
returned value is "new Date()" result when
-the message has been instantiated
+Fix MIME4J-262 without breaking compatibility with old code by deprecating 
MessageBuilder and adding Message.Builder
+
+Message.Builder produce message for which the getDate method return null if 
and only if the message do not have a "Date"
+header. Whereas MessageBuilder will produce message for which the getDate 
method never return null: if the message has
+no "Date" header the returned value is "new Date()" result when the message 
has been instantiated
 
 Release 0.7.2
 -------------------

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/dom/Message.java
----------------------------------------------------------------------
diff --git a/dom/src/main/java/org/apache/james/mime4j/dom/Message.java 
b/dom/src/main/java/org/apache/james/mime4j/dom/Message.java
index 17132e4..6084dd1 100644
--- a/dom/src/main/java/org/apache/james/mime4j/dom/Message.java
+++ b/dom/src/main/java/org/apache/james/mime4j/dom/Message.java
@@ -24,6 +24,45 @@ import java.util.Date;
 import org.apache.james.mime4j.dom.address.AddressList;
 import org.apache.james.mime4j.dom.address.Mailbox;
 import org.apache.james.mime4j.dom.address.MailboxList;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.TimeZone;
+
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.MimeIOException;
+import org.apache.james.mime4j.codec.DecodeMonitor;
+import org.apache.james.mime4j.dom.address.Address;
+import org.apache.james.mime4j.dom.field.AddressListField;
+import org.apache.james.mime4j.dom.field.DateTimeField;
+import org.apache.james.mime4j.dom.field.FieldName;
+import org.apache.james.mime4j.dom.field.MailboxField;
+import org.apache.james.mime4j.dom.field.MailboxListField;
+import org.apache.james.mime4j.dom.field.ParseException;
+import org.apache.james.mime4j.dom.field.UnstructuredField;
+import org.apache.james.mime4j.field.DefaultFieldParser;
+import org.apache.james.mime4j.field.Fields;
+import org.apache.james.mime4j.field.LenientFieldParser;
+import org.apache.james.mime4j.field.address.DefaultAddressParser;
+import org.apache.james.mime4j.io.InputStreams;
+import org.apache.james.mime4j.internal.AbstractEntityBuilder;
+import org.apache.james.mime4j.message.BasicBodyFactory;
+import org.apache.james.mime4j.message.BodyFactory;
+import org.apache.james.mime4j.message.DefaultBodyDescriptorBuilder;
+import org.apache.james.mime4j.message.HeaderImpl;
+import org.apache.james.mime4j.message.MessageImpl;
+import org.apache.james.mime4j.message.MultipartBuilder;
+import org.apache.james.mime4j.internal.ParserStreamContentHandler;
+import org.apache.james.mime4j.parser.MimeStreamParser;
+import org.apache.james.mime4j.stream.BodyDescriptorBuilder;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.MimeConfig;
+import org.apache.james.mime4j.stream.NameValuePair;
 
 /**
  * An MIME message (as defined in RFC 2045).
@@ -108,4 +147,878 @@ public interface Message extends Entity, Body {
      */
     AddressList getReplyTo();
 
+    class Builder extends AbstractEntityBuilder {
+        private MimeConfig config;
+        private DecodeMonitor monitor;
+        private BodyDescriptorBuilder bodyDescBuilder;
+        private FieldParser<?> fieldParser;
+        private BodyFactory bodyFactory;
+        private boolean flatMode;
+        private boolean rawContent;
+
+        private Builder() {
+            super();
+        }
+
+        public static Builder of() {
+            return new Builder();
+        }
+
+        public static Builder of(Message other) {
+            return new Builder().copy(other);
+        }
+
+        public static Builder of(final InputStream is) throws IOException {
+            return new Builder().parse(is);
+        }
+
+        /**
+         * Sets MIME configuration.
+         *
+         * @param config the configuration.
+         */
+        public Builder use(MimeConfig config) {
+            this.config = config;
+            return this;
+        }
+
+        /**
+         * Sets {@link org.apache.james.mime4j.codec.DecodeMonitor} that will 
be
+         * used to handle malformed data when executing {@link 
#parse(java.io.InputStream)}.
+         *
+         * @param monitor the decoder monitor.
+         */
+        public Builder use(DecodeMonitor monitor) {
+            this.monitor = monitor;
+            return this;
+        }
+
+        /**
+         * Sets {@link org.apache.james.mime4j.stream.BodyDescriptorBuilder} 
that will be
+         * used to generate body descriptors when executing {@link 
#parse(java.io.InputStream)}.
+         *
+         * @param bodyDescBuilder the body descriptor builder.
+         */
+        public Builder use(BodyDescriptorBuilder bodyDescBuilder) {
+            this.bodyDescBuilder = bodyDescBuilder;
+            return this;
+        }
+
+        /**
+         * Sets {@link org.apache.james.mime4j.dom.FieldParser} that will be
+         * used to generate parse message fields when executing {@link 
#parse(java.io.InputStream)}.
+         *
+         * @param fieldParser the field parser.
+         */
+        public Builder use(FieldParser<?> fieldParser) {
+            this.fieldParser = fieldParser;
+            return this;
+        }
+
+        /**
+         * Sets {@link org.apache.james.mime4j.message.BodyFactory} that will 
be
+         * used to generate message body.
+         *
+         * @param bodyFactory the body factory.
+         */
+        public Builder use(BodyFactory bodyFactory) {
+            this.bodyFactory = bodyFactory;
+            return this;
+        }
+
+        /**
+         * Enables flat parsing mode for {@link #parse(java.io.InputStream)} 
operation.
+         */
+        public Builder enableFlatMode() {
+            this.flatMode = true;
+            return this;
+        }
+
+        /**
+         * Disables flat parsing mode for {@link #parse(java.io.InputStream)} 
operation.
+         */
+        public Builder disableFlatMode() {
+            this.flatMode = false;
+            return this;
+        }
+
+        /**
+         * Enables automatic content decoding for {@link 
#parse(java.io.InputStream)} operation.
+         */
+        public Builder enableContentDecoding() {
+            this.rawContent = false;
+            return this;
+        }
+
+        /**
+         * Enables disable content decoding for {@link 
#parse(java.io.InputStream)} operation.
+         */
+        public Builder disableContentDecoding() {
+            this.rawContent = true;
+            return this;
+        }
+
+        public Builder copy(Message other) {
+            if (other == null) {
+                return this;
+            }
+            clearFields();
+            final Header otherHeader = other.getHeader();
+            if (otherHeader != null) {
+                final List<Field> otherFields = otherHeader.getFields();
+                for (Field field: otherFields) {
+                    addField(field);
+                }
+            }
+            Body body = null;
+            Body otherBody = other.getBody();
+            if (otherBody instanceof Message) {
+                body = Builder.of((Message) otherBody).build();
+            } else if (otherBody instanceof Multipart) {
+                body = MultipartBuilder.createCopy((Multipart) 
otherBody).build();
+            } else if (otherBody instanceof SingleBody) {
+                body = ((SingleBody) otherBody).copy();
+            }
+            setBody(body);
+            return this;
+        }
+
+        @Override
+        public Builder setField(Field field) {
+            super.setField(field);
+            return this;
+        }
+
+        @Override
+        public Builder addField(Field field) {
+            super.addField(field);
+            return this;
+        }
+
+        @Override
+        public Builder removeFields(String name) {
+            super.removeFields(name);
+            return this;
+        }
+
+        @Override
+        public Builder clearFields() {
+            super.clearFields();
+            return this;
+        }
+
+        @Override
+        public Builder setContentTransferEncoding(String 
contentTransferEncoding) {
+            super.setContentTransferEncoding(contentTransferEncoding);
+            return this;
+        }
+
+        @Override
+        public Builder setContentType(String mimeType, NameValuePair... 
parameters) {
+            super.setContentType(mimeType, parameters);
+            return this;
+        }
+
+        @Override
+        public Builder setContentDisposition(String dispositionType) {
+            super.setContentDisposition(dispositionType);
+            return this;
+        }
+
+        @Override
+        public Builder setContentDisposition(String dispositionType, String 
filename) {
+            super.setContentDisposition(dispositionType, filename);
+            return this;
+        }
+
+        @Override
+        public Builder setContentDisposition(String dispositionType, String 
filename, long size) {
+            super.setContentDisposition(dispositionType, filename, size);
+            return this;
+        }
+
+        @Override
+        public Builder setContentDisposition(String dispositionType, String 
filename, long size,
+                                                    Date creationDate, Date 
modificationDate, Date readDate) {
+            super.setContentDisposition(dispositionType, filename, size, 
creationDate, modificationDate, readDate);
+            return this;
+        }
+
+        @Override
+        public Builder setBody(Body body) {
+            super.setBody(body);
+            return this;
+        }
+
+        @Override
+        public Builder setBody(TextBody textBody) {
+            super.setBody(textBody);
+            return this;
+        }
+
+        @Override
+        public Builder setBody(BinaryBody binaryBody) {
+            super.setBody(binaryBody);
+            return this;
+        }
+
+        @Override
+        public Builder setBody(Multipart multipart) {
+            super.setBody(multipart);
+            return this;
+        }
+
+        @Override
+        public Builder setBody(Message message) {
+            super.setBody(message);
+            return this;
+        }
+
+        /**
+         * Sets text of this message with the charset.
+         *
+         * @param text
+         *            the text.
+         * @param charset
+         *            the charset of the text.
+         */
+        public Builder setBody(String text, Charset charset) throws 
IOException {
+            return setBody(text, null, charset);
+        }
+
+        /**
+         * Sets text of this message with the given MIME subtype and charset.
+         *
+         * @param text
+         *            the text.
+         * @param charset
+         *            the charset of the text.
+         * @param subtype
+         *            the text subtype (e.g. &quot;plain&quot;, 
&quot;html&quot; or
+         *            &quot;xml&quot;).
+         */
+        public Builder setBody(String text, String subtype, Charset charset) 
throws IOException {
+            String mimeType = "text/" + (subtype != null ? subtype : "plain");
+            if (charset != null) {
+                setField(Fields.contentType(mimeType, new 
NameValuePair("charset", charset.name())));
+            } else {
+                setField(Fields.contentType(mimeType));
+            }
+            Body textBody;
+            if (bodyFactory != null) {
+                textBody = bodyFactory.textBody(
+                    InputStreams.create(text, charset),
+                    charset != null ? charset.name() : null);
+            } else {
+                textBody = BasicBodyFactory.INSTANCE.textBody(text, charset);
+            }
+            return setBody(textBody);
+        }
+
+        /**
+         * Sets binary content of this message with the given MIME type.
+         *
+         * @param bin
+         *            the body.
+         * @param mimeType
+         *            the MIME media type of the specified body
+         *            (&quot;type/subtype&quot;).
+         */
+        public Builder setBody(byte[] bin, String mimeType) throws IOException 
{
+            setField(Fields.contentType(mimeType != null ? mimeType : 
"application/octet-stream"));
+            Body binBody;
+            if (bodyFactory != null) {
+                binBody = bodyFactory.binaryBody(InputStreams.create(bin));
+            } else {
+                binBody = BasicBodyFactory.INSTANCE.binaryBody(bin);
+            }
+            return setBody(binBody);
+        }
+
+        /**
+         * Returns the value of the <i>Message-ID</i> header field of this 
message
+         * or <code>null</code> if it is not present.
+         *
+         * @return the identifier of this message.
+         */
+        public String getMessageId() {
+            Field field = obtainField(FieldName.MESSAGE_ID);
+            return field != null ? field.getBody() : null;
+        }
+
+        /**
+         * Generates and sets message ID for this message.
+         *
+         * @param hostname
+         *            host name to be included in the identifier or
+         *            <code>null</code> if no host name should be included.
+         */
+        public Builder generateMessageId(String hostname) {
+            if (hostname == null) {
+                removeFields(FieldName.MESSAGE_ID);
+            } else {
+                setField(Fields.generateMessageId(hostname));
+            }
+            return this;
+        }
+
+        /**
+         * Sets message ID for this message.
+         *
+         * @param messageId
+         *            the message ID.
+         */
+        public Builder setMessageId(String messageId) {
+            if (messageId == null) {
+                removeFields(FieldName.MESSAGE_ID);
+            } else {
+                setField(Fields.messageId(messageId));
+            }
+            return this;
+        }
+
+        /**
+         * Returns the (decoded) value of the <i>Subject</i> header field of 
this
+         * message or <code>null</code> if it is not present.
+         *
+         * @return the subject of this message.
+         */
+        public String getSubject() {
+            UnstructuredField field = obtainField(FieldName.SUBJECT);
+            return field != null ? field.getValue() : null;
+        }
+
+        /**
+         * Sets <i>Subject</i> header field for this message. The specified
+         * string may contain non-ASCII characters, in which case it gets 
encoded as
+         * an 'encoded-word' automatically.
+         *
+         * @param subject
+         *            subject to set or <code>null</code> to remove the subject
+         *            header field.
+         */
+        public Builder setSubject(String subject) {
+            if (subject == null) {
+                removeFields(FieldName.SUBJECT);
+            } else {
+                setField(Fields.subject(subject));
+            }
+            return this;
+        }
+
+        /**
+         * Returns the value of the <i>Date</i> header field of this message as
+         * <code>Date</code> object or <code>null</code> if it is not present.
+         *
+         * @return the date of this message.
+         */
+        public Date getDate() {
+            DateTimeField field = obtainField(FieldName.DATE);
+            return field != null ? field.getDate() : null;
+        }
+
+        /**
+         * Sets <i>Date</i> header field for this message. This method uses the
+         * default <code>TimeZone</code> of this host to encode the specified
+         * <code>Date</code> object into a string.
+         *
+         * @param date
+         *            date to set or <code>null</code> to remove the date 
header
+         *            field.
+         */
+        public Builder setDate(Date date) {
+            return setDate(date, null);
+        }
+
+        /**
+         * Sets <i>Date</i> header field for this message. The specified
+         * <code>TimeZone</code> is used to encode the specified 
<code>Date</code>
+         * object into a string.
+         *
+         * @param date
+         *            date to set or <code>null</code> to remove the date 
header
+         *            field.
+         * @param zone
+         *            a time zone.
+         */
+        public Builder setDate(Date date, TimeZone zone) {
+            if (date == null) {
+                removeFields(FieldName.DATE);
+            } else {
+                setField(Fields.date(FieldName.DATE, date, zone));
+            }
+            return this;
+        }
+
+        /**
+         * Returns the value of the <i>Sender</i> header field of this message 
as
+         * <code>Mailbox</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return the sender of this message.
+         */
+        public Mailbox getSender() {
+            return getMailbox(FieldName.SENDER);
+        }
+
+        /**
+         * Sets <i>Sender</i> header field of this message to the specified
+         * mailbox address.
+         *
+         * @param sender
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setSender(Mailbox sender) {
+            return setMailbox(FieldName.SENDER, sender);
+        }
+
+        /**
+         * Sets <i>Sender</i> header field of this message to the specified
+         * mailbox address.
+         *
+         * @param sender
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setSender(String sender) throws ParseException {
+            return setMailbox(FieldName.SENDER, sender);
+        }
+
+        /**
+         * Returns the value of the <i>From</i> header field of this message as
+         * <code>MailboxList</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return value of the from field of this message.
+         */
+        public MailboxList getFrom() {
+            return getMailboxList(FieldName.FROM);
+        }
+
+        /**
+         * Sets <i>From</i> header field of this message to the specified
+         * mailbox address.
+         *
+         * @param from
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setFrom(Mailbox from) {
+            return setMailboxList(FieldName.FROM, from);
+        }
+
+        /**
+         * Sets <i>From</i> header field of this message to the specified
+         * mailbox address.
+         *
+         * @param from
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setFrom(String from) throws ParseException {
+            return setMailboxList(FieldName.FROM, from);
+        }
+
+        /**
+         * Sets <i>From</i> header field of this message to the specified
+         * mailbox addresses.
+         *
+         * @param from
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setFrom(Mailbox... from) {
+            return setMailboxList(FieldName.FROM, from);
+        }
+
+        /**
+         * Sets <i>From</i> header field of this message to the specified
+         * mailbox addresses.
+         *
+         * @param from
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setFrom(String... from) throws ParseException {
+            return setMailboxList(FieldName.FROM, from);
+        }
+
+        /**
+         * Sets <i>From</i> header field of this message to the specified
+         * mailbox addresses.
+         *
+         * @param from
+         *            addresses to set or <code>null</code> or an empty 
collection
+         *            to remove the header field.
+         */
+        public Builder setFrom(Collection<Mailbox> from) {
+            return setMailboxList(FieldName.FROM, from);
+        }
+
+        /**
+         * Returns the value of the <i>To</i> header field of this message as
+         * <code>AddressList</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return value of the to field of this message.
+         */
+        public AddressList getTo() {
+            return getAddressList(FieldName.TO);
+        }
+
+        /**
+         * Sets <i>To</i> header field of this message to the specified
+         * address.
+         *
+         * @param to
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setTo(Address to) {
+            return setAddressList(FieldName.TO, to);
+        }
+
+        /**
+         * Sets <i>To</i> header field of this message to the specified
+         * address.
+         *
+         * @param to
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setTo(String to) throws ParseException {
+            return setAddressList(FieldName.TO, to);
+        }
+
+        /**
+         * Sets <i>To</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param to
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setTo(Address... to) {
+            return setAddressList(FieldName.TO, to);
+        }
+
+        /**
+         * Sets <i>To</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param to
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setTo(String... to) throws ParseException {
+            return setAddressList(FieldName.TO, to);
+        }
+
+        /**
+         * Sets <i>To</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param to
+         *            addresses to set or <code>null</code> or an empty 
collection
+         *            to remove the header field.
+         */
+        public Builder setTo(Collection<? extends Address> to) {
+            return setAddressList(FieldName.TO, to);
+        }
+
+        /**
+         * Returns the value of the <i>Cc</i> header field of this message as
+         * <code>AddressList</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return value of the cc field of this message.
+         */
+        public AddressList getCc() {
+            return getAddressList(FieldName.CC);
+        }
+
+        /**
+         * Sets <i>Cc</i> header field of this message to the specified
+         * address.
+         *
+         * @param cc
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setCc(Address cc) {
+            return setAddressList(FieldName.CC, cc);
+        }
+
+        /**
+         * Sets <i>Cc</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param cc
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setCc(Address... cc) {
+            return setAddressList(FieldName.CC, cc);
+        }
+
+        /**
+         * Sets <i>Cc</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param cc
+         *            addresses to set or <code>null</code> or an empty 
collection
+         *            to remove the header field.
+         */
+        public Builder setCc(Collection<? extends Address> cc) {
+            return setAddressList(FieldName.CC, cc);
+        }
+
+        /**
+         * Returns the value of the <i>Bcc</i> header field of this message as
+         * <code>AddressList</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return value of the bcc field of this message.
+         */
+        public AddressList getBcc() {
+            return getAddressList(FieldName.BCC);
+        }
+
+        /**
+         * Sets <i>Bcc</i> header field of this message to the specified
+         * address.
+         *
+         * @param bcc
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setBcc(Address bcc) {
+            return setAddressList(FieldName.BCC, bcc);
+        }
+
+        /**
+         * Sets <i>Bcc</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param bcc
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setBcc(Address... bcc) {
+            return setAddressList(FieldName.BCC, bcc);
+        }
+
+        /**
+         * Sets <i>Bcc</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param bcc
+         *            addresses to set or <code>null</code> or an empty 
collection
+         *            to remove the header field.
+         */
+        public Builder setBcc(Collection<? extends Address> bcc) {
+            return setAddressList(FieldName.BCC, bcc);
+        }
+
+        /**
+         * Returns the value of the <i>Reply-To</i> header field of this 
message as
+         * <code>AddressList</code> object or <code>null</code> if it is not
+         * present.
+         *
+         * @return value of the reply to field of this message.
+         */
+        public AddressList getReplyTo() {
+            return getAddressList(FieldName.REPLY_TO);
+        }
+
+        /**
+         * Sets <i>Reply-To</i> header field of this message to the specified
+         * address.
+         *
+         * @param replyTo
+         *            address to set or <code>null</code> to remove the header
+         *            field.
+         */
+        public Builder setReplyTo(Address replyTo) {
+            return setAddressList(FieldName.REPLY_TO, replyTo);
+        }
+
+        /**
+         * Sets <i>Reply-To</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param replyTo
+         *            addresses to set or <code>null</code> or no arguments to
+         *            remove the header field.
+         */
+        public Builder setReplyTo(Address... replyTo) {
+            return setAddressList(FieldName.REPLY_TO, replyTo);
+        }
+
+        /**
+         * Sets <i>Reply-To</i> header field of this message to the specified
+         * addresses.
+         *
+         * @param replyTo
+         *            addresses to set or <code>null</code> or an empty 
collection
+         *            to remove the header field.
+         */
+        public Builder setReplyTo(Collection<? extends Address> replyTo) {
+            return setAddressList(FieldName.REPLY_TO, replyTo);
+        }
+
+        public Builder parse(final InputStream is) throws IOException {
+            MimeConfig currentConfig = config != null ? config : 
MimeConfig.DEFAULT;
+            boolean strict = currentConfig.isStrictParsing();
+            DecodeMonitor currentMonitor = monitor != null ? monitor : strict 
? DecodeMonitor.STRICT : DecodeMonitor.SILENT;
+            BodyDescriptorBuilder currentBodyDescBuilder = bodyDescBuilder != 
null ? bodyDescBuilder :
+                new DefaultBodyDescriptorBuilder(null, fieldParser != null ? 
fieldParser :
+                    strict ? DefaultFieldParser.getParser() : 
LenientFieldParser.getParser(), currentMonitor);
+            BodyFactory currentBodyFactory = bodyFactory != null ? bodyFactory 
: new BasicBodyFactory(!strict);
+            MimeStreamParser parser = new MimeStreamParser(currentConfig, 
currentMonitor, currentBodyDescBuilder);
+
+            Message message = new MessageImpl();
+            parser.setContentHandler(new ParserStreamContentHandler(message, 
currentBodyFactory));
+            parser.setContentDecoding(!rawContent);
+            if (flatMode) {
+                parser.setFlat();
+            }
+            try {
+                parser.parse(is);
+            } catch (MimeException e) {
+                throw new MimeIOException(e);
+            }
+            clearFields();
+            final List<Field> fields = message.getHeader().getFields();
+            for (Field field: fields) {
+                addField(field);
+            }
+            setBody(message.getBody());
+            return this;
+        }
+
+        public Message build() {
+            MessageImpl message = new MessageImpl();
+            HeaderImpl header = new HeaderImpl();
+            message.setHeader(header);
+            if (!containsField(FieldName.MIME_VERSION)) {
+                header.setField(Fields.version("1.0"));
+            }
+            for (Field field : getFields()) {
+                header.addField(field);
+            }
+
+            message.setBody(getBody());
+
+            return message;
+        }
+
+        private Mailbox getMailbox(String fieldName) {
+            MailboxField field = obtainField(fieldName);
+            return field != null ? field.getMailbox() : null;
+        }
+
+        private Builder setMailbox(String fieldName, Mailbox mailbox) {
+            if (mailbox == null) {
+                removeFields(fieldName);
+            } else {
+                setField(Fields.mailbox(fieldName, mailbox));
+            }
+            return this;
+        }
+
+        private Builder setMailbox(String fieldName, String mailbox) throws 
ParseException {
+            if (mailbox == null) {
+                removeFields(fieldName);
+            } else {
+                setField(Fields.mailbox(fieldName, 
DefaultAddressParser.DEFAULT.parseMailbox(mailbox)));
+            }
+            return this;
+        }
+
+        private MailboxList getMailboxList(String fieldName) {
+            MailboxListField field = obtainField(fieldName);
+            return field != null ? field.getMailboxList() : null;
+        }
+
+        private Builder setMailboxList(String fieldName, Mailbox mailbox) {
+            return setMailboxList(fieldName, mailbox == null ? null : 
Collections.singleton(mailbox));
+        }
+
+        private Builder setMailboxList(String fieldName, String mailbox) 
throws ParseException {
+            return setMailboxList(fieldName, mailbox == null ? null : 
DefaultAddressParser.DEFAULT.parseMailbox(mailbox));
+        }
+
+        private Builder setMailboxList(String fieldName, Mailbox... mailboxes) 
{
+            return setMailboxList(fieldName, mailboxes == null ? null : 
Arrays.asList(mailboxes));
+        }
+
+        private List<Mailbox> parseMailboxes(String... mailboxes) throws 
ParseException {
+            if (mailboxes == null || mailboxes.length == 0) {
+                return null;
+            } else {
+                List<Mailbox> list = new ArrayList<Mailbox>();
+                for (String mailbox: mailboxes) {
+                    
list.add(DefaultAddressParser.DEFAULT.parseMailbox(mailbox));
+                }
+                return list;
+            }
+        }
+
+        private Builder setMailboxList(String fieldName, String... mailboxes) 
throws ParseException {
+            return setMailboxList(fieldName, parseMailboxes(mailboxes));
+        }
+
+        private Builder setMailboxList(String fieldName, Collection<Mailbox> 
mailboxes) {
+            if (mailboxes == null || mailboxes.isEmpty()) {
+                removeFields(fieldName);
+            } else {
+                setField(Fields.mailboxList(fieldName, mailboxes));
+            }
+            return this;
+        }
+
+        private AddressList getAddressList(String fieldName) {
+            AddressListField field = obtainField(fieldName);
+            return field != null? field.getAddressList() : null;
+        }
+
+        private Builder setAddressList(String fieldName, Address address) {
+            return setAddressList(fieldName, address == null ? null : 
Collections.singleton(address));
+        }
+
+        private Builder setAddressList(String fieldName, String address) 
throws ParseException {
+            return setAddressList(fieldName, address == null ? null : 
DefaultAddressParser.DEFAULT.parseMailbox(address));
+        }
+
+        private Builder setAddressList(String fieldName, Address... addresses) 
{
+            return setAddressList(fieldName, addresses == null ? null : 
Arrays.asList(addresses));
+        }
+
+        private List<Address> parseAddresses(String... addresses) throws 
ParseException {
+            if (addresses == null || addresses.length == 0) {
+                return null;
+            } else {
+                List<Address> list = new ArrayList<Address>();
+                for (String address: addresses) {
+                    
list.add(DefaultAddressParser.DEFAULT.parseAddress(address));
+                }
+                return list;
+            }
+        }
+
+        private Builder setAddressList(String fieldName, String... addresses) 
throws ParseException {
+            return setAddressList(fieldName, parseAddresses(addresses));
+        }
+
+        private Builder setAddressList(String fieldName, Collection<? extends 
Address> addresses) {
+            if (addresses == null || addresses.isEmpty()) {
+                removeFields(fieldName);
+            } else {
+                setField(Fields.addressList(fieldName, addresses));
+            }
+            return this;
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/internal/AbstractEntityBuilder.java
----------------------------------------------------------------------
diff --git 
a/dom/src/main/java/org/apache/james/mime4j/internal/AbstractEntityBuilder.java 
b/dom/src/main/java/org/apache/james/mime4j/internal/AbstractEntityBuilder.java
new file mode 100644
index 0000000..011a2d7
--- /dev/null
+++ 
b/dom/src/main/java/org/apache/james/mime4j/internal/AbstractEntityBuilder.java
@@ -0,0 +1,576 @@
+/****************************************************************
+ * 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.internal;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+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;
+import org.apache.james.mime4j.dom.TextBody;
+import org.apache.james.mime4j.dom.field.ContentDispositionField;
+import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
+import org.apache.james.mime4j.dom.field.ContentTypeField;
+import org.apache.james.mime4j.dom.field.FieldName;
+import org.apache.james.mime4j.dom.field.ParsedField;
+import org.apache.james.mime4j.field.Fields;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.stream.NameValuePair;
+import org.apache.james.mime4j.util.MimeUtil;
+
+public abstract class AbstractEntityBuilder {
+
+    private final List<Field> fields;
+    private final Map<String, List<Field>> fieldMap;
+
+    private Body body;
+
+    public AbstractEntityBuilder() {
+        this.fields = new LinkedList<Field>();
+        this.fieldMap = new HashMap<String, List<Field>>();
+    }
+
+    /**
+     * Adds a field to the end of the list of fields.
+     *
+     * @param field the field to add.
+     */
+    public AbstractEntityBuilder addField(Field field) {
+        List<Field> values = 
fieldMap.get(field.getName().toLowerCase(Locale.US));
+        if (values == null) {
+            values = new LinkedList<Field>();
+            fieldMap.put(field.getName().toLowerCase(Locale.US), values);
+        }
+        values.add(field);
+        fields.add(field);
+        return this;
+    }
+
+    /**
+     * Gets the fields of this header. The returned list will not be
+     * modifiable.
+     *
+     * @return the list of <code>Field</code> objects.
+     */
+    public List<Field> getFields() {
+        return Collections.unmodifiableList(fields);
+    }
+
+    /**
+     * Gets a <code>Field</code> given a field name. If there are multiple
+     * such fields defined in this header the first one will be returned.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @return the field or <code>null</code> if none found.
+     */
+    public Field getField(String name) {
+        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
+        if (l != null && !l.isEmpty()) {
+            return l.get(0);
+        }
+        return null;
+    }
+
+    /**
+     * Gets a <code>Field</code> given a field name and of the given type.
+     * If there are multiple such fields defined in this header the first
+     * one will be returned.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @param clazz the field class.
+     * @return the field or <code>null</code> if none found.
+     */
+    public <F extends Field> F getField(final String name, final Class<F> 
clazz) {
+        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
+        for (int i = 0; i < l.size(); i++) {
+            Field field = l.get(i);
+            if (clazz.isInstance(field)) {
+                return clazz.cast(field);
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Returns <code>true<code/> if there is at least one explicitly
+     * set field with the given name.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @return <code>true<code/> if there is at least one explicitly
+     * set field with the given name, <code>false<code/> otherwise.
+     */
+    public boolean containsField(String name) {
+        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
+        return l != null && !l.isEmpty();
+    }
+
+    /**
+     * Gets all <code>Field</code>s having the specified field name.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @return the list of fields.
+     */
+    public List<Field> getFields(final String name) {
+        final String lowerCaseName = name.toLowerCase(Locale.US);
+        final List<Field> l = fieldMap.get(lowerCaseName);
+        final List<Field> results;
+        if (l == null || l.isEmpty()) {
+            results = Collections.emptyList();
+        } else {
+            results = Collections.unmodifiableList(l);
+        }
+        return results;
+    }
+
+    /**
+     * Gets all <code>Field</code>s having the specified field name
+     * and of the given type.
+     *
+     * @param name the field name (e.g. From, Subject).
+     * @param clazz the field class.
+     * @return the list of fields.
+     */
+    public <F extends Field> List<F> getFields(final String name, final 
Class<F> clazz) {
+        final String lowerCaseName = name.toLowerCase(Locale.US);
+        final List<Field> l = fieldMap.get(lowerCaseName);
+        if (l == null) {
+            return Collections.emptyList();
+        }
+        final List<F> results = new ArrayList<F>();
+        for (int i = 0; i < l.size(); i++) {
+            Field field = l.get(i);
+            if (clazz.isInstance(field)) {
+                results.add(clazz.cast(field));
+            }
+        }
+        return results;
+    }
+
+    /**
+     * Removes all <code>Field</code>s having the specified field name.
+     *
+     * @param name
+     *            the field name (e.g. From, Subject).
+     */
+    public AbstractEntityBuilder removeFields(String name) {
+        final String lowerCaseName = name.toLowerCase(Locale.US);
+        List<Field> removed = fieldMap.remove(lowerCaseName);
+        if (removed == null || removed.isEmpty()) {
+            return this;
+        }
+        for (Iterator<Field> iterator = fields.iterator(); 
iterator.hasNext();) {
+            Field field = iterator.next();
+            if (field.getName().equalsIgnoreCase(name)) {
+                iterator.remove();
+            }
+        }
+        return this;
+    }
+
+    /**
+     * Sets or replaces a field. This method is useful for header fields such 
as
+     * Subject or Message-ID that should not occur more than once in a message.
+     *
+     * If this builder does not already contain a header field of
+     * the same name as the given field then it is added to the end of the list
+     * of fields (same behavior as {@link 
#addField(org.apache.james.mime4j.stream.Field)}). Otherwise the
+     * first occurrence of a field with the same name is replaced by the given
+     * field and all further occurrences are removed.
+     *
+     * @param field the field to set.
+     */
+    public AbstractEntityBuilder setField(Field field) {
+        final String lowerCaseName = field.getName().toLowerCase(Locale.US);
+        List<Field> l = fieldMap.get(lowerCaseName);
+        if (l == null || l.isEmpty()) {
+            addField(field);
+            return this;
+        }
+
+        l.clear();
+        l.add(field);
+
+        int firstOccurrence = -1;
+        int index = 0;
+        for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext(); 
index++) {
+            Field f = iterator.next();
+            if (f.getName().equalsIgnoreCase(field.getName())) {
+                iterator.remove();
+                if (firstOccurrence == -1) {
+                    firstOccurrence = index;
+                }
+            }
+        }
+        fields.add(firstOccurrence, field);
+        return this;
+    }
+
+    /**
+     * Clears all fields.
+     */
+    public AbstractEntityBuilder clearFields() {
+        fields.clear();
+        fieldMap.clear();
+        return this;
+    }
+
+    @SuppressWarnings("unchecked")
+    public <F extends ParsedField> F obtainField(String fieldName) {
+        return (F) getField(fieldName);
+    }
+
+    /**
+     * Returns MIME type of this message.
+     *
+     * @return the MIME type or <code>null</code> if no MIME
+     *         type has been set.
+     */
+    public String getMimeType() {
+        ContentTypeField field = obtainField(FieldName.CONTENT_TYPE);
+        return field != null ? field.getMimeType() : null;
+    }
+
+    /**
+     * Returns MIME character set encoding of this message.
+     *
+     * @return the MIME character set encoding or <code>null</code> if no 
charset
+     *         type has been set.
+     */
+    public String getCharset() {
+        ContentTypeField field = obtainField(FieldName.CONTENT_TYPE);
+        return field != null ? field.getCharset() : null;
+    }
+
+    /**
+     * Sets transfer encoding of this message.
+     *
+     * @param mimeType MIME type of this message
+     *            the MIME type to use.
+     * @param parameters content type parameters to use.
+     */
+    public AbstractEntityBuilder setContentType(String mimeType, 
NameValuePair... parameters) {
+        if (mimeType == null) {
+            removeFields(FieldName.CONTENT_TYPE);
+        } else {
+            setField(Fields.contentType(mimeType, parameters));
+        }
+        return this;
+    }
+
+    /**
+     * Returns transfer encoding of this message.
+     *
+     * @return the transfer encoding.
+     */
+    public String getContentTransferEncoding() {
+        ContentTransferEncodingField field = 
obtainField(FieldName.CONTENT_TRANSFER_ENCODING);
+        return field != null ? field.getEncoding() : null;
+    }
+
+    /**
+     * Sets transfer encoding of this message.
+     *
+     * @param contentTransferEncoding
+     *            transfer encoding to use.
+     */
+    public AbstractEntityBuilder setContentTransferEncoding(String 
contentTransferEncoding) {
+        if (contentTransferEncoding == null) {
+            removeFields(FieldName.CONTENT_TRANSFER_ENCODING);
+        } else {
+            setField(Fields.contentTransferEncoding(contentTransferEncoding));
+        }
+        return this;
+    }
+
+    /**
+     * Return disposition type of this message.
+     *
+     * @return the disposition type or <code>null</code> if no disposition
+     *         type has been set.
+     */
+    public String getDispositionType() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getDispositionType() : null;
+    }
+
+    /**
+     * Sets content disposition of this message to the
+     * specified disposition type. No filename, size or date parameters
+     * are included in the content disposition.
+     *
+     * @param dispositionType
+     *            disposition type value (usually <code>inline</code> or
+     *            <code>attachment</code>).
+     */
+    public AbstractEntityBuilder setContentDisposition(String dispositionType) 
{
+        if (dispositionType == null) {
+            removeFields(FieldName.CONTENT_DISPOSITION);
+        } else {
+            setField(Fields.contentDisposition(dispositionType));
+        }
+        return this;
+    }
+
+    /**
+     * Sets content disposition of this message to the
+     * specified disposition type and filename. No size or date parameters are
+     * included in the content disposition.
+     *
+     * @param dispositionType
+     *            disposition type value (usually <code>inline</code> or
+     *            <code>attachment</code>).
+     * @param filename
+     *            filename parameter value or <code>null</code> if the
+     *            parameter should not be included.
+     */
+    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename) {
+        if (dispositionType == null) {
+            removeFields(FieldName.CONTENT_DISPOSITION);
+        } else {
+            setField(Fields.contentDisposition(dispositionType, filename));
+        }
+        return this;
+    }
+
+    /**
+     * Sets content disposition of this message to the
+     * specified values. No date parameters are included in the content
+     * disposition.
+     *
+     * @param dispositionType
+     *            disposition type value (usually <code>inline</code> or
+     *            <code>attachment</code>).
+     * @param filename
+     *            filename parameter value or <code>null</code> if the
+     *            parameter should not be included.
+     * @param size
+     *            size parameter value or <code>-1</code> if the parameter
+     *            should not be included.
+     */
+    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename,
+                                      long size) {
+        if (dispositionType == null) {
+            removeFields(FieldName.CONTENT_DISPOSITION);
+        } else {
+            setField(Fields.contentDisposition(dispositionType, filename, 
size));
+        }
+        return this;
+    }
+
+    /**
+     * Sets content disposition of this message to the
+     * specified values.
+     *
+     * @param dispositionType
+     *            disposition type value (usually <code>inline</code> or
+     *            <code>attachment</code>).
+     * @param filename
+     *            filename parameter value or <code>null</code> if the
+     *            parameter should not be included.
+     * @param size
+     *            size parameter value or <code>-1</code> if the parameter
+     *            should not be included.
+     * @param creationDate
+     *            creation-date parameter value or <code>null</code> if the
+     *            parameter should not be included.
+     * @param modificationDate
+     *            modification-date parameter value or <code>null</code> if
+     *            the parameter should not be included.
+     * @param readDate
+     *            read-date parameter value or <code>null</code> if the
+     *            parameter should not be included.
+     */
+    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename,
+                                      long size, Date creationDate, Date 
modificationDate, Date readDate) {
+        if (dispositionType == null) {
+            removeFields(FieldName.CONTENT_DISPOSITION);
+        } else {
+            setField(Fields.contentDisposition(dispositionType, filename, size,
+                    creationDate, modificationDate, readDate));
+        }
+        return this;
+    }
+
+    /**
+     * Returns filename of the content disposition of this message.
+     *
+     * @return the filename parameter of the content disposition or
+     *         <code>null</code> if the filename has not been set.
+     */
+    public String getFilename() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getFilename() : null;
+    }
+
+    /**
+     * Returns size of the content disposition of this message.
+     *
+     * @return the size parameter of the content disposition or
+     *         <code>-1</code> if the filename has not been set.
+     */
+    public long getSize() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getSize() : -1;
+    }
+
+    /**
+     * Returns creation date of the content disposition of this message.
+     *
+     * @return the creation date parameter of the content disposition or
+     *         <code>null</code> if the filename has not been set.
+     */
+    public Date getCreationDate() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getCreationDate() : null;
+    }
+
+    /**
+     * Returns modification date of the content disposition of this message.
+     *
+     * @return the modification date parameter of the content disposition or
+     *         <code>null</code> if the filename has not been set.
+     */
+    public Date getModificationDate() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getModificationDate() : null;
+    }
+
+    /**
+     * Returns read date of the content disposition of this message.
+     *
+     * @return the read date parameter of the content disposition or
+     *         <code>null</code> if the filename has not been set.
+     */
+    public Date getReadDate() {
+        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
+        return field != null ? field.getReadDate() : null;
+    }
+
+    /**
+     * 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(Body body) {
+        this.body = 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 {
+            removeFields(FieldName.CONTENT_TYPE);
+        }
+        return this;
+    }
+
+    /**
+     * Returns message body.
+     *
+     * @return the message body.
+     */
+    public Body getBody() {
+        return body;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/internal/ParserStreamContentHandler.java
----------------------------------------------------------------------
diff --git 
a/dom/src/main/java/org/apache/james/mime4j/internal/ParserStreamContentHandler.java
 
b/dom/src/main/java/org/apache/james/mime4j/internal/ParserStreamContentHandler.java
new file mode 100644
index 0000000..d251ba7
--- /dev/null
+++ 
b/dom/src/main/java/org/apache/james/mime4j/internal/ParserStreamContentHandler.java
@@ -0,0 +1,188 @@
+/****************************************************************
+ * 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.internal;
+
+import org.apache.james.mime4j.MimeException;
+import org.apache.james.mime4j.dom.Body;
+import org.apache.james.mime4j.dom.Entity;
+import org.apache.james.mime4j.dom.Header;
+import org.apache.james.mime4j.dom.Message;
+import org.apache.james.mime4j.dom.Multipart;
+import org.apache.james.mime4j.message.BodyFactory;
+import org.apache.james.mime4j.message.BodyPart;
+import org.apache.james.mime4j.message.DefaultMessageImplFactory;
+import org.apache.james.mime4j.message.HeaderImpl;
+import org.apache.james.mime4j.message.MessageImplFactory;
+import org.apache.james.mime4j.message.MultipartImpl;
+import org.apache.james.mime4j.parser.ContentHandler;
+import org.apache.james.mime4j.stream.BodyDescriptor;
+import org.apache.james.mime4j.stream.Field;
+import org.apache.james.mime4j.util.ByteArrayBuffer;
+import org.apache.james.mime4j.util.ByteSequence;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Stack;
+
+/**
+ * A <code>ContentHandler</code> for building an <code>Entity</code> to be
+ * used in conjunction with a {@link 
org.apache.james.mime4j.parser.MimeStreamParser}.
+ */
+public class ParserStreamContentHandler implements ContentHandler {
+
+    private final Entity entity;
+    private final MessageImplFactory messageImplFactory;
+    private final BodyFactory bodyFactory;
+    private final Stack<Object> stack;
+
+    public ParserStreamContentHandler(
+            final Entity entity,
+            final BodyFactory bodyFactory) {
+        this.entity = entity;
+        this.messageImplFactory = new DefaultMessageImplFactory();
+        this.bodyFactory = bodyFactory;
+        this.stack = new Stack<Object>();
+    }
+
+    public ParserStreamContentHandler(
+            final Entity entity,
+            final MessageImplFactory messageImplFactory,
+            final BodyFactory bodyFactory) {
+        this.entity = entity;
+        this.messageImplFactory = messageImplFactory;
+        this.bodyFactory = bodyFactory;
+        this.stack = new Stack<Object>();
+    }
+
+    private void expect(Class<?> c) {
+        if (!c.isInstance(stack.peek())) {
+            throw new IllegalStateException("Internal stack error: "
+                    + "Expected '" + c.getName() + "' found '"
+                    + stack.peek().getClass().getName() + "'");
+        }
+    }
+
+    public void startMessage() throws MimeException {
+        if (stack.isEmpty()) {
+            stack.push(this.entity);
+        } else {
+            expect(Entity.class);
+            Message m = messageImplFactory.messageImpl();
+            ((Entity) stack.peek()).setBody(m);
+            stack.push(m);
+        }
+    }
+
+    public void endMessage() throws MimeException {
+        expect(Message.class);
+        stack.pop();
+    }
+
+    public void startHeader() throws MimeException {
+        stack.push(new HeaderImpl());
+    }
+
+    public void field(Field field) throws MimeException {
+        expect(Header.class);
+        ((Header) stack.peek()).addField(field);
+    }
+
+    public void endHeader() throws MimeException {
+        expect(Header.class);
+        Header h = (Header) stack.pop();
+        expect(Entity.class);
+        ((Entity) stack.peek()).setHeader(h);
+    }
+
+    public void startMultipart(final BodyDescriptor bd) throws MimeException {
+        expect(Entity.class);
+
+        final Entity e = (Entity) stack.peek();
+        final String subType = bd.getSubType();
+        final Multipart multiPart = new MultipartImpl(subType);
+        e.setBody(multiPart);
+        stack.push(multiPart);
+    }
+
+    public void body(BodyDescriptor bd, final InputStream is) throws 
MimeException, IOException {
+        expect(Entity.class);
+
+        final Body body;
+        if (bd.getMimeType().startsWith("text/")) {
+            body = bodyFactory.textBody(is, bd.getCharset());
+        } else {
+            body = bodyFactory.binaryBody(is);
+        }
+
+        Entity entity = ((Entity) stack.peek());
+        entity.setBody(body);
+    }
+
+    public void endMultipart() throws MimeException {
+        stack.pop();
+    }
+
+    public void startBodyPart() throws MimeException {
+        expect(Multipart.class);
+
+        BodyPart bodyPart = new BodyPart();
+        ((Multipart) stack.peek()).addBodyPart(bodyPart);
+        stack.push(bodyPart);
+    }
+
+    public void endBodyPart() throws MimeException {
+        expect(BodyPart.class);
+        stack.pop();
+    }
+
+    public void epilogue(InputStream is) throws MimeException, IOException {
+        expect(MultipartImpl.class);
+        ByteSequence bytes = loadStream(is);
+        ((MultipartImpl) stack.peek()).setEpilogueRaw(bytes);
+    }
+
+    public void preamble(InputStream is) throws MimeException, IOException {
+        expect(MultipartImpl.class);
+        ByteSequence bytes = loadStream(is);
+        ((MultipartImpl) stack.peek()).setPreambleRaw(bytes);
+    }
+
+    /**
+     * Unsupported.
+     *
+     * @param is the raw contents of the entity.
+     * @throws UnsupportedOperationException
+     */
+    public void raw(InputStream is) throws MimeException, IOException {
+        throw new UnsupportedOperationException("Not supported");
+    }
+
+    private static ByteSequence loadStream(InputStream in) throws IOException {
+        ByteArrayBuffer bab = new ByteArrayBuffer(64);
+
+        int b;
+        while ((b = in.read()) != -1) {
+            bab.append(b);
+        }
+
+        return bab;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java
----------------------------------------------------------------------
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
deleted file mode 100644
index 55f736f..0000000
--- 
a/dom/src/main/java/org/apache/james/mime4j/message/AbstractEntityBuilder.java
+++ /dev/null
@@ -1,576 +0,0 @@
-/****************************************************************
- * 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.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-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;
-import org.apache.james.mime4j.dom.TextBody;
-import org.apache.james.mime4j.dom.field.ContentDispositionField;
-import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
-import org.apache.james.mime4j.dom.field.ContentTypeField;
-import org.apache.james.mime4j.dom.field.FieldName;
-import org.apache.james.mime4j.dom.field.ParsedField;
-import org.apache.james.mime4j.field.Fields;
-import org.apache.james.mime4j.stream.Field;
-import org.apache.james.mime4j.stream.NameValuePair;
-import org.apache.james.mime4j.util.MimeUtil;
-
-abstract class AbstractEntityBuilder {
-
-    private final List<Field> fields;
-    private final Map<String, List<Field>> fieldMap;
-
-    private Body body;
-
-    AbstractEntityBuilder() {
-        this.fields = new LinkedList<Field>();
-        this.fieldMap = new HashMap<String, List<Field>>();
-    }
-
-    /**
-     * Adds a field to the end of the list of fields.
-     *
-     * @param field the field to add.
-     */
-    public AbstractEntityBuilder addField(Field field) {
-        List<Field> values = 
fieldMap.get(field.getName().toLowerCase(Locale.US));
-        if (values == null) {
-            values = new LinkedList<Field>();
-            fieldMap.put(field.getName().toLowerCase(Locale.US), values);
-        }
-        values.add(field);
-        fields.add(field);
-        return this;
-    }
-
-    /**
-     * Gets the fields of this header. The returned list will not be
-     * modifiable.
-     *
-     * @return the list of <code>Field</code> objects.
-     */
-    public List<Field> getFields() {
-        return Collections.unmodifiableList(fields);
-    }
-
-    /**
-     * Gets a <code>Field</code> given a field name. If there are multiple
-     * such fields defined in this header the first one will be returned.
-     *
-     * @param name the field name (e.g. From, Subject).
-     * @return the field or <code>null</code> if none found.
-     */
-    public Field getField(String name) {
-        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
-        if (l != null && !l.isEmpty()) {
-            return l.get(0);
-        }
-        return null;
-    }
-
-    /**
-     * Gets a <code>Field</code> given a field name and of the given type.
-     * If there are multiple such fields defined in this header the first
-     * one will be returned.
-     *
-     * @param name the field name (e.g. From, Subject).
-     * @param clazz the field class.
-     * @return the field or <code>null</code> if none found.
-     */
-    public <F extends Field> F getField(final String name, final Class<F> 
clazz) {
-        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
-        for (int i = 0; i < l.size(); i++) {
-            Field field = l.get(i);
-            if (clazz.isInstance(field)) {
-                return clazz.cast(field);
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns <code>true<code/> if there is at least one explicitly
-     * set field with the given name.
-     *
-     * @param name the field name (e.g. From, Subject).
-     * @return <code>true<code/> if there is at least one explicitly
-     * set field with the given name, <code>false<code/> otherwise.
-     */
-    public boolean containsField(String name) {
-        List<Field> l = fieldMap.get(name.toLowerCase(Locale.US));
-        return l != null && !l.isEmpty();
-    }
-
-    /**
-     * Gets all <code>Field</code>s having the specified field name.
-     *
-     * @param name the field name (e.g. From, Subject).
-     * @return the list of fields.
-     */
-    public List<Field> getFields(final String name) {
-        final String lowerCaseName = name.toLowerCase(Locale.US);
-        final List<Field> l = fieldMap.get(lowerCaseName);
-        final List<Field> results;
-        if (l == null || l.isEmpty()) {
-            results = Collections.emptyList();
-        } else {
-            results = Collections.unmodifiableList(l);
-        }
-        return results;
-    }
-
-    /**
-     * Gets all <code>Field</code>s having the specified field name
-     * and of the given type.
-     *
-     * @param name the field name (e.g. From, Subject).
-     * @param clazz the field class.
-     * @return the list of fields.
-     */
-    public <F extends Field> List<F> getFields(final String name, final 
Class<F> clazz) {
-        final String lowerCaseName = name.toLowerCase(Locale.US);
-        final List<Field> l = fieldMap.get(lowerCaseName);
-        if (l == null) {
-            return Collections.emptyList();
-        }
-        final List<F> results = new ArrayList<F>();
-        for (int i = 0; i < l.size(); i++) {
-            Field field = l.get(i);
-            if (clazz.isInstance(field)) {
-                results.add(clazz.cast(field));
-            }
-        }
-        return results;
-    }
-
-    /**
-     * Removes all <code>Field</code>s having the specified field name.
-     *
-     * @param name
-     *            the field name (e.g. From, Subject).
-     */
-    public AbstractEntityBuilder removeFields(String name) {
-        final String lowerCaseName = name.toLowerCase(Locale.US);
-        List<Field> removed = fieldMap.remove(lowerCaseName);
-        if (removed == null || removed.isEmpty()) {
-            return this;
-        }
-        for (Iterator<Field> iterator = fields.iterator(); 
iterator.hasNext();) {
-            Field field = iterator.next();
-            if (field.getName().equalsIgnoreCase(name)) {
-                iterator.remove();
-            }
-        }
-        return this;
-    }
-
-    /**
-     * Sets or replaces a field. This method is useful for header fields such 
as
-     * Subject or Message-ID that should not occur more than once in a message.
-     *
-     * If this builder does not already contain a header field of
-     * the same name as the given field then it is added to the end of the list
-     * of fields (same behavior as {@link 
#addField(org.apache.james.mime4j.stream.Field)}). Otherwise the
-     * first occurrence of a field with the same name is replaced by the given
-     * field and all further occurrences are removed.
-     *
-     * @param field the field to set.
-     */
-    public AbstractEntityBuilder setField(Field field) {
-        final String lowerCaseName = field.getName().toLowerCase(Locale.US);
-        List<Field> l = fieldMap.get(lowerCaseName);
-        if (l == null || l.isEmpty()) {
-            addField(field);
-            return this;
-        }
-
-        l.clear();
-        l.add(field);
-
-        int firstOccurrence = -1;
-        int index = 0;
-        for (Iterator<Field> iterator = fields.iterator(); iterator.hasNext(); 
index++) {
-            Field f = iterator.next();
-            if (f.getName().equalsIgnoreCase(field.getName())) {
-                iterator.remove();
-                if (firstOccurrence == -1) {
-                    firstOccurrence = index;
-                }
-            }
-        }
-        fields.add(firstOccurrence, field);
-        return this;
-    }
-
-    /**
-     * Clears all fields.
-     */
-    public AbstractEntityBuilder clearFields() {
-        fields.clear();
-        fieldMap.clear();
-        return this;
-    }
-
-    @SuppressWarnings("unchecked")
-    <F extends ParsedField> F obtainField(String fieldName) {
-        return (F) getField(fieldName);
-    }
-
-    /**
-     * Returns MIME type of this message.
-     *
-     * @return the MIME type or <code>null</code> if no MIME
-     *         type has been set.
-     */
-    public String getMimeType() {
-        ContentTypeField field = obtainField(FieldName.CONTENT_TYPE);
-        return field != null ? field.getMimeType() : null;
-    }
-
-    /**
-     * Returns MIME character set encoding of this message.
-     *
-     * @return the MIME character set encoding or <code>null</code> if no 
charset
-     *         type has been set.
-     */
-    public String getCharset() {
-        ContentTypeField field = obtainField(FieldName.CONTENT_TYPE);
-        return field != null ? field.getCharset() : null;
-    }
-
-    /**
-     * Sets transfer encoding of this message.
-     *
-     * @param mimeType MIME type of this message
-     *            the MIME type to use.
-     * @param parameters content type parameters to use.
-     */
-    public AbstractEntityBuilder setContentType(String mimeType, 
NameValuePair... parameters) {
-        if (mimeType == null) {
-            removeFields(FieldName.CONTENT_TYPE);
-        } else {
-            setField(Fields.contentType(mimeType, parameters));
-        }
-        return this;
-    }
-
-    /**
-     * Returns transfer encoding of this message.
-     *
-     * @return the transfer encoding.
-     */
-    public String getContentTransferEncoding() {
-        ContentTransferEncodingField field = 
obtainField(FieldName.CONTENT_TRANSFER_ENCODING);
-        return field != null ? field.getEncoding() : null;
-    }
-
-    /**
-     * Sets transfer encoding of this message.
-     *
-     * @param contentTransferEncoding
-     *            transfer encoding to use.
-     */
-    public AbstractEntityBuilder setContentTransferEncoding(String 
contentTransferEncoding) {
-        if (contentTransferEncoding == null) {
-            removeFields(FieldName.CONTENT_TRANSFER_ENCODING);
-        } else {
-            setField(Fields.contentTransferEncoding(contentTransferEncoding));
-        }
-        return this;
-    }
-
-    /**
-     * Return disposition type of this message.
-     *
-     * @return the disposition type or <code>null</code> if no disposition
-     *         type has been set.
-     */
-    public String getDispositionType() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getDispositionType() : null;
-    }
-
-    /**
-     * Sets content disposition of this message to the
-     * specified disposition type. No filename, size or date parameters
-     * are included in the content disposition.
-     *
-     * @param dispositionType
-     *            disposition type value (usually <code>inline</code> or
-     *            <code>attachment</code>).
-     */
-    public AbstractEntityBuilder setContentDisposition(String dispositionType) 
{
-        if (dispositionType == null) {
-            removeFields(FieldName.CONTENT_DISPOSITION);
-        } else {
-            setField(Fields.contentDisposition(dispositionType));
-        }
-        return this;
-    }
-
-    /**
-     * Sets content disposition of this message to the
-     * specified disposition type and filename. No size or date parameters are
-     * included in the content disposition.
-     *
-     * @param dispositionType
-     *            disposition type value (usually <code>inline</code> or
-     *            <code>attachment</code>).
-     * @param filename
-     *            filename parameter value or <code>null</code> if the
-     *            parameter should not be included.
-     */
-    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename) {
-        if (dispositionType == null) {
-            removeFields(FieldName.CONTENT_DISPOSITION);
-        } else {
-            setField(Fields.contentDisposition(dispositionType, filename));
-        }
-        return this;
-    }
-
-    /**
-     * Sets content disposition of this message to the
-     * specified values. No date parameters are included in the content
-     * disposition.
-     *
-     * @param dispositionType
-     *            disposition type value (usually <code>inline</code> or
-     *            <code>attachment</code>).
-     * @param filename
-     *            filename parameter value or <code>null</code> if the
-     *            parameter should not be included.
-     * @param size
-     *            size parameter value or <code>-1</code> if the parameter
-     *            should not be included.
-     */
-    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename,
-                                      long size) {
-        if (dispositionType == null) {
-            removeFields(FieldName.CONTENT_DISPOSITION);
-        } else {
-            setField(Fields.contentDisposition(dispositionType, filename, 
size));
-        }
-        return this;
-    }
-
-    /**
-     * Sets content disposition of this message to the
-     * specified values.
-     *
-     * @param dispositionType
-     *            disposition type value (usually <code>inline</code> or
-     *            <code>attachment</code>).
-     * @param filename
-     *            filename parameter value or <code>null</code> if the
-     *            parameter should not be included.
-     * @param size
-     *            size parameter value or <code>-1</code> if the parameter
-     *            should not be included.
-     * @param creationDate
-     *            creation-date parameter value or <code>null</code> if the
-     *            parameter should not be included.
-     * @param modificationDate
-     *            modification-date parameter value or <code>null</code> if
-     *            the parameter should not be included.
-     * @param readDate
-     *            read-date parameter value or <code>null</code> if the
-     *            parameter should not be included.
-     */
-    public AbstractEntityBuilder setContentDisposition(String dispositionType, 
String filename,
-                                      long size, Date creationDate, Date 
modificationDate, Date readDate) {
-        if (dispositionType == null) {
-            removeFields(FieldName.CONTENT_DISPOSITION);
-        } else {
-            setField(Fields.contentDisposition(dispositionType, filename, size,
-                    creationDate, modificationDate, readDate));
-        }
-        return this;
-    }
-
-    /**
-     * Returns filename of the content disposition of this message.
-     *
-     * @return the filename parameter of the content disposition or
-     *         <code>null</code> if the filename has not been set.
-     */
-    public String getFilename() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getFilename() : null;
-    }
-
-    /**
-     * Returns size of the content disposition of this message.
-     *
-     * @return the size parameter of the content disposition or
-     *         <code>-1</code> if the filename has not been set.
-     */
-    public long getSize() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getSize() : -1;
-    }
-
-    /**
-     * Returns creation date of the content disposition of this message.
-     *
-     * @return the creation date parameter of the content disposition or
-     *         <code>null</code> if the filename has not been set.
-     */
-    public Date getCreationDate() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getCreationDate() : null;
-    }
-
-    /**
-     * Returns modification date of the content disposition of this message.
-     *
-     * @return the modification date parameter of the content disposition or
-     *         <code>null</code> if the filename has not been set.
-     */
-    public Date getModificationDate() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getModificationDate() : null;
-    }
-
-    /**
-     * Returns read date of the content disposition of this message.
-     *
-     * @return the read date parameter of the content disposition or
-     *         <code>null</code> if the filename has not been set.
-     */
-    public Date getReadDate() {
-        ContentDispositionField field = 
obtainField(FieldName.CONTENT_DISPOSITION);
-        return field != null ? field.getReadDate() : null;
-    }
-
-    /**
-     * 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(Body body) {
-        this.body = 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 {
-            removeFields(FieldName.CONTENT_TYPE);
-        }
-        return this;
-    }
-
-    /**
-     * Returns message body.
-     *
-     * @return the message body.
-     */
-    public Body getBody() {
-        return body;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/message/BodyPartBuilder.java
----------------------------------------------------------------------
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 2864cc1..837592f 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
@@ -29,6 +29,7 @@ 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.internal.AbstractEntityBuilder;
 import org.apache.james.mime4j.io.InputStreams;
 import org.apache.james.mime4j.stream.Field;
 import org.apache.james.mime4j.stream.NameValuePair;

http://git-wip-us.apache.org/repos/asf/james-mime4j/blob/5c583037/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageBuilder.java
----------------------------------------------------------------------
diff --git 
a/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageBuilder.java 
b/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageBuilder.java
index 323e25c..0f35dab 100644
--- 
a/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageBuilder.java
+++ 
b/dom/src/main/java/org/apache/james/mime4j/message/DefaultMessageBuilder.java
@@ -37,6 +37,7 @@ import org.apache.james.mime4j.dom.SingleBody;
 import org.apache.james.mime4j.dom.field.ParsedField;
 import org.apache.james.mime4j.field.DefaultFieldParser;
 import org.apache.james.mime4j.field.LenientFieldParser;
+import org.apache.james.mime4j.internal.ParserStreamContentHandler;
 import org.apache.james.mime4j.parser.AbstractContentHandler;
 import org.apache.james.mime4j.parser.MimeStreamParser;
 import org.apache.james.mime4j.stream.BodyDescriptorBuilder;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to