/****************************************************************
* 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.Header;
import org.apache.james.mime4j.dom.Message;
import org.apache.james.mime4j.dom.Multipart;
import org.apache.james.mime4j.dom.SingleBody;
import org.apache.james.mime4j.dom.field.Field;
import org.apache.james.mime4j.field.DefaultFieldParser;
import org.apache.james.mime4j.parser.AbstractContentHandler;
import org.apache.james.mime4j.parser.MimeStreamParser;
import org.apache.james.mime4j.stream.MimeEntityConfig;
import org.apache.james.mime4j.stream.MutableBodyDescriptorFactory;
import org.apache.james.mime4j.stream.RawField;
/**
* Utility class for copying message and parsing message elements.
*/
public class MimeBuilder {
public static final MimeBuilder DEFAULT = new MimeBuilder();
protected MimeBuilder() {
super();
}
/**
* Creates a new Header
from the specified
* Header
. The Header
instance is initialized
* with a copy of the list of {@link Field}s of the specified
* Header
. The Field
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 BodyPart
from the specified
* Entity
. The BodyPart
instance is initialized
* with copies of header and body of the specified Entity
.
* The parent entity of the new body part is null
.
*
* @param other
* body part to copy.
* @throws UnsupportedOperationException
* if other
contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if other
contains a Body
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 Multipart
from the specified
* Multipart
. The Multipart
instance is
* initialized with copies of preamble, epilogue, sub type and the list of
* body parts of the specified Multipart
. The parent entity
* of the new multipart is null
.
*
* @param other
* multipart to copy.
* @throws UnsupportedOperationException
* if other
contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if other
contains a Body
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.
*
* The {@link Body#getParent() parent} of the returned copy is
* null
, 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 body
is an instance of {@link SingleBody}
* that does not support the {@link SingleBody#copy() copy()}
* operation (or contains such a SingleBody
).
* @throws IllegalArgumentException
* if body
is null
or
* body
is a Body
that is neither
* a {@link MessageImpl}, {@link Multipart} or {@link SingleBody}
* (or contains such a Body
).
*/
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 Message
from the specified
* Message
. The Message
instance is
* initialized with copies of header and body of the specified
* Message
. The parent entity of the new message is
* null
.
*
* @param other
* message to copy.
* @throws UnsupportedOperationException
* if other
contains a {@link SingleBody} that
* does not support the {@link SingleBody#copy() copy()}
* operation.
* @throws IllegalArgumentException
* if other
contains a Body
that
* is neither a {@link MessageImpl}, {@link Multipart} or
* {@link SingleBody}.
*/
public Message copy(Message other) {
MessageImpl copy = new MessageImpl();
if (other.getHeader() != null) {
copy.setHeader(copy(other.getHeader()));
}
if (other.getBody() != null) {
copy.setBody(copy(other.getBody()));
}
return copy;
}
/**
* Creates a new Header
from the specified stream.
*
* @param is the stream to read the header from.
*
* @throws IOException on I/O errors.
* @throws MimeIOException on MIME protocol violations.
*/
public Header parse(
final InputStream is,
final DecodeMonitor monitor) throws IOException, MimeIOException {
final HeaderImpl header = new HeaderImpl();
final MimeStreamParser parser = new MimeStreamParser();
parser.setContentHandler(new AbstractContentHandler() {
@Override
public void endHeader() {
parser.stop();
}
@Override
public void field(RawField field) throws MimeException {
Field parsedField = DefaultFieldParser.parse(field.getRaw(), monitor);
header.addField(parsedField);
}
});
try {
parser.parse(is);
} catch (MimeException ex) {
throw new MimeIOException(ex);
}
return header;
}
/**
* Parses the specified MIME message stream into a Message
* instance using given {@link MimeEntityConfig} and {@link StorageProvider}.
*
* @param is
* the stream to parse.
* @param config
* {@link MimeEntityConfig} to use.
* @param bodyFactory
* {@link BodyFactory} to use for storing text and binary
* message bodies.
* @param bodyDescFactory
* {@link MutableBodyDescriptorFactory} to use for creating body descriptors.
* @throws IOException
* on I/O errors.
* @throws MimeIOException
* on MIME protocol violations.
*/
public Message parse(
final InputStream is,
final MimeEntityConfig config,
final BodyFactory bodyFactory,
final MutableBodyDescriptorFactory bodyDescFactory,
final boolean contentDecoding,
final boolean flatMode,
final DecodeMonitor monitor) throws IOException, MimeIOException {
try {
MessageImpl message = new MessageImpl();
DecodeMonitor mon = monitor != null ? monitor : DecodeMonitor.SILENT;
MimeStreamParser parser = new MimeStreamParser(config, bodyDescFactory, mon);
parser.setContentHandler(new EntityBuilder(message, bodyFactory, mon));
parser.setContentDecoding(contentDecoding);
if (flatMode) {
parser.setFlat();
} else {
parser.setRecurse();
}
parser.parse(is);
return message;
} catch (MimeException e) {
throw new MimeIOException(e);
}
}
/**
* Parses the specified MIME message stream into a Message
* instance using given {@link MimeEntityConfig} and {@link StorageProvider}.
*
* @param is
* the stream to parse.
* @param config
* {@link MimeEntityConfig} to use.
* @param storageProvider
* {@link StorageProvider} to use for storing text and binary
* message bodies.
* @param bodyDescFactory
* {@link MutableBodyDescriptorFactory} to use for creating body descriptors.
* @throws IOException
* on I/O errors.
* @throws MimeIOException
* on MIME protocol violations.
*/
public Message parse(
final InputStream is,
final MimeEntityConfig config,
final BodyFactory bodyFactory,
final MutableBodyDescriptorFactory bodyDescFactory,
final DecodeMonitor monitor) throws IOException, MimeIOException {
return parse(is, config, bodyFactory, bodyDescFactory, true, false, monitor);
}
public Message parse(
final InputStream is,
final MimeEntityConfig config,
final BodyFactory bodyFactory,
final MutableBodyDescriptorFactory bodyDescFactory) throws IOException, MimeIOException {
return parse(is, config, bodyFactory, bodyDescFactory, null);
}
public Message parse(
final InputStream is,
final MimeEntityConfig config,
final BodyFactory bodyFactory) throws IOException, MimeIOException {
return parse(is, config, bodyFactory, null, null);
}
/**
* Parses the specified MIME message stream into a Message
* instance.
*
* @param is
* the stream to parse.
* @throws IOException
* on I/O errors.
* @throws MimeIOException
* on MIME protocol violations.
*/
public Message parse(InputStream is) throws IOException, MimeIOException {
return parse(is, null, null);
}
/**
* Parses the specified MIME message stream into a Message
* instance using given {@link MimeEntityConfig}.
*
* @param is
* the stream to parse.
* @throws IOException
* on I/O errors.
* @throws MimeIOException
* on MIME protocol violations.
*/
public Message parse(InputStream is, MimeEntityConfig config) throws IOException,
MimeIOException {
return parse(is, config, null);
}
}