blob: 6221a30a8b725d19fff11a41fe77939f30e42ad2 [file] [log] [blame]
/****************************************************************
* 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.IOException;
import java.io.InputStream;
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.Body;
import org.apache.james.mime4j.dom.Disposable;
import org.apache.james.mime4j.dom.Entity;
import org.apache.james.mime4j.dom.FieldParser;
import org.apache.james.mime4j.dom.Header;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.MessageBuilder;
import org.apache.james.mime4j.dom.Multipart;
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.parser.AbstractContentHandler;
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;
/**
* Default implementation of {@link MessageBuilder}.
*/
public class DefaultMessageBuilder implements MessageBuilder {
private FieldParser<? extends ParsedField> fieldParser = null;
private MessageImplFactory messageImplFactory = null;
private BodyFactory bodyFactory = null;
private MimeConfig config = null;
private BodyDescriptorBuilder bodyDescBuilder = null;
private boolean contentDecoding = true;
private boolean flatMode = false;
private DecodeMonitor monitor = null;
public DefaultMessageBuilder() {
super();
}
public void setFieldParser(final FieldParser<? extends ParsedField> fieldParser) {
this.fieldParser = fieldParser;
}
public void setMessageImplFactory(final MessageImplFactory messageImplFactory) {
this.messageImplFactory = messageImplFactory;
}
public void setBodyFactory(final BodyFactory bodyFactory) {
this.bodyFactory = bodyFactory;
}
public void setMimeEntityConfig(final MimeConfig config) {
this.config = config;
}
public void setBodyDescriptorBuilder(final BodyDescriptorBuilder bodyDescBuilder) {
this.bodyDescBuilder = bodyDescBuilder;
}
public void setDecodeMonitor(final DecodeMonitor monitor) {
this.monitor = monitor;
}
public void setContentDecoding(boolean contentDecoding) {
this.contentDecoding = contentDecoding;
}
public void setFlatMode(boolean flatMode) {
this.flatMode = flatMode;
}
/**
* Creates a new <code>Header</code> from the specified
* <code>Header</code>. The <code>Header</code> instance is initialized
* with a copy of the list of {@link Field}s of the specified
* <code>Header</code>. The <code>Field</code> objects are not copied
* because they are immutable and can safely be shared between headers.
*
* @param other
* header to copy.
*/
public Header copy(Header other) {
HeaderImpl copy = new HeaderImpl();
for (Field otherField : other.getFields()) {
copy.addField(otherField);
}
return copy;
}
/**
* Creates a new <code>BodyPart</code> from the specified
* <code>Entity</code>. The <code>BodyPart</code> instance is initialized
* with copies of header and body of the specified <code>Entity</code>.
* The parent entity of the new body part is <code>null</code>.
*
* @param other
* body part to copy.
* @throws UnsupportedOperationException
* if <code>other</code> contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if <code>other</code> contains a <code>Body</code> that
* is neither a {@link Message}, {@link Multipart} or
* {@link SingleBody}.
*/
public BodyPart copy(Entity other) {
BodyPart copy = new BodyPart();
if (other.getHeader() != null) {
copy.setHeader(copy(other.getHeader()));
}
if (other.getBody() != null) {
copy.setBody(copy(other.getBody()));
}
return copy;
}
/**
* Creates a new <code>Multipart</code> from the specified
* <code>Multipart</code>. The <code>Multipart</code> instance is
* initialized with copies of preamble, epilogue, sub type and the list of
* body parts of the specified <code>Multipart</code>. The parent entity
* of the new multipart is <code>null</code>.
*
* @param other
* multipart to copy.
* @throws UnsupportedOperationException
* if <code>other</code> contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if <code>other</code> contains a <code>Body</code> that
* is neither a {@link Message}, {@link Multipart} or
* {@link SingleBody}.
*/
public Multipart copy(Multipart other) {
MultipartImpl copy = new MultipartImpl(other.getSubType());
for (Entity otherBodyPart : other.getBodyParts()) {
copy.addBodyPart(copy(otherBodyPart));
}
copy.setPreamble(other.getPreamble());
copy.setEpilogue(other.getEpilogue());
return copy;
}
/**
* Returns a copy of the given {@link Body} that can be used (and modified)
* independently of the original. The copy should be
* {@link Disposable#dispose() disposed of} when it is no longer needed.
* <p>
* The {@link Body#getParent() parent} of the returned copy is
* <code>null</code>, that is, the copy is detached from the parent
* entity of the original.
*
* @param body
* body to copy.
* @return a copy of the given body.
* @throws UnsupportedOperationException
* if <code>body</code> is an instance of {@link SingleBody}
* that does not support the {@link SingleBody#copy() copy()}
* operation (or contains such a <code>SingleBody</code>).
* @throws IllegalArgumentException
* if <code>body</code> is <code>null</code> or
* <code>body</code> is a <code>Body</code> that is neither
* a {@link MessageImpl}, {@link Multipart} or {@link SingleBody}
* (or contains such a <code>Body</code>).
*/
public Body copy(Body body) {
if (body == null)
throw new IllegalArgumentException("Body is null");
if (body instanceof Message)
return copy((Message) body);
if (body instanceof Multipart)
return copy((Multipart) body);
if (body instanceof SingleBody)
return ((SingleBody) body).copy();
throw new IllegalArgumentException("Unsupported body class");
}
/**
* Creates a new <code>Message</code> from the specified
* <code>Message</code>. The <code>Message</code> instance is
* initialized with copies of header and body of the specified
* <code>Message</code>. The parent entity of the new message is
* <code>null</code>.
*
* @param other
* message to copy.
* @throws UnsupportedOperationException
* if <code>other</code> contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if <code>other</code> contains a <code>Body</code> that
* is neither a {@link MessageImpl}, {@link Multipart} or
* {@link SingleBody}.
*/
public Message copy(Message other) {
MessageImpl copy = newMessageImpl();
if (other.getHeader() != null) {
copy.setHeader(copy(other.getHeader()));
}
if (other.getBody() != null) {
copy.setBody(copy(other.getBody()));
}
return copy;
}
public Header newHeader() {
return new HeaderImpl();
}
public Header newHeader(final Header source) {
return copy(source);
}
public Multipart newMultipart(final String subType) {
return new MultipartImpl(subType);
}
public Multipart newMultipart(final Multipart source) {
return copy(source);
}
public Header parseHeader(final InputStream is) throws IOException, MimeIOException {
final MimeConfig cfg = config != null ? config : MimeConfig.DEFAULT;
boolean strict = cfg.isStrictParsing();
final DecodeMonitor mon = monitor != null ? monitor :
strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT;
final FieldParser<? extends ParsedField> fp = fieldParser != null ? fieldParser :
strict ? DefaultFieldParser.getParser() : LenientFieldParser.getParser();
final HeaderImpl header = new HeaderImpl();
final MimeStreamParser parser = new MimeStreamParser(cfg, mon, null);
parser.setContentHandler(new AbstractContentHandler() {
@Override
public void endHeader() {
parser.stop();
}
@Override
public void field(Field field) throws MimeException {
ParsedField parsedField;
if (field instanceof ParsedField) {
parsedField = (ParsedField) field;
} else {
parsedField = fp.parse(field, mon);
}
header.addField(parsedField);
}
});
try {
parser.parse(is);
} catch (MimeException ex) {
throw new MimeIOException(ex);
}
return header;
}
public Message newMessage() {
return newMessageImpl();
}
public Message newMessage(final Message source) {
return copy(source);
}
public Message parseMessage(final InputStream is) throws IOException, MimeIOException {
try {
MessageImpl message = newMessageImpl();
MimeConfig cfg = config != null ? config : MimeConfig.DEFAULT;
boolean strict = cfg.isStrictParsing();
DecodeMonitor mon = monitor != null ? monitor :
strict ? DecodeMonitor.STRICT : DecodeMonitor.SILENT;
BodyDescriptorBuilder bdb = bodyDescBuilder != null ? bodyDescBuilder :
new DefaultBodyDescriptorBuilder(null, fieldParser != null ? fieldParser :
strict ? DefaultFieldParser.getParser() : LenientFieldParser.getParser(), mon);
BodyFactory bf = bodyFactory != null ? bodyFactory : new BasicBodyFactory();
MimeStreamParser parser = new MimeStreamParser(cfg, mon, bdb);
parser.setContentHandler(new ParserStreamContentHandler(message, bf));
parser.setContentDecoding(contentDecoding);
if (flatMode) {
parser.setFlat();
} else {
parser.setRecurse();
}
parser.parse(is);
return message;
} catch (MimeException e) {
throw new MimeIOException(e);
}
}
private MessageImpl newMessageImpl() {
MessageImplFactory mif = messageImplFactory != null ? messageImplFactory : new DefaultMessageImplFactory();
return mif.messageImpl();
}
}