AttachBuilder.java

package fr.sii.ogham.email.message.fluent;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;

import fr.sii.ogham.core.resource.FileResource;
import fr.sii.ogham.core.resource.LookupResource;
import fr.sii.ogham.core.resource.OverrideNameWrapper;
import fr.sii.ogham.core.resource.path.ResourcePath;
import fr.sii.ogham.core.resource.path.UnresolvedPath;
import fr.sii.ogham.email.attachment.Attachment;
import fr.sii.ogham.email.attachment.ContentDisposition;
import fr.sii.ogham.email.message.Email;

/**
 * Fluent API to attach a file to the email.
 * 
 * @author Aurélien Baudet
 * @since 3.0.0
 */
public class AttachBuilder {
	private final Email parent;
	private final List<Attachment> attachments;

	/**
	 * Initializes with the parent to go back to.
	 * 
	 * @param parent
	 *            the parent instance
	 */
	public AttachBuilder(Email parent) {
		super();
		this.parent = parent;
		this.attachments = new ArrayList<>();
	}

	/**
	 * Attach a file to the email. The name of the attachment uses the name of
	 * the file.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param file
	 *            the file to attach
	 * @return the email instance for fluent chaining
	 */
	public Email file(File file) {
		return file(file, null);
	}

	/**
	 * Attach a file to the email. The name of the attachment uses the name of
	 * the file.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param file
	 *            the file to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email file(File file, String contentType) {
		attach(new Attachment(file), contentType);
		return parent;
	}

	/**
	 * Attach a file to the email. The name of the attachment is explicitly
	 * specified.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param file
	 *            the file to attach
	 * @return the email instance for fluent chaining
	 */
	public Email file(String customName, File file) {
		return file(customName, file, null);
	}

	/**
	 * Attach a file to the email. The name of the attachment is explicitly
	 * specified.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param file
	 *            the file to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email file(String customName, File file, String contentType) {
		attach(new Attachment(new FileResource(file, customName)), contentType);
		return parent;
	}

	/**
	 * Attach a file to the email. The name of the attachment uses the name of
	 * the file.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param path
	 *            the path to the file to attach
	 * @return the email instance for fluent chaining
	 */
	public Email file(Path path) {
		return file(path, null);
	}

	/**
	 * Attach a file to the email. The name of the attachment uses the name of
	 * the file.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param path
	 *            the path to the file to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email file(Path path, String contentType) {
		return file(path.toFile(), contentType);
	}

	/**
	 * Attach a file to the email. The name of the attachment is explicitly
	 * specified.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the file to attach
	 * @return the email instance for fluent chaining
	 */
	public Email file(String customName, Path path) {
		return file(customName, path, null);
	}

	/**
	 * Attach a file to the email. The name of the attachment is explicitly
	 * specified.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the file to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email file(String customName, Path path, String contentType) {
		return file(customName, path.toFile(), contentType);
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment uses the name of the resource (extracted from the path).
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param path
	 *            the path to the resource to attach
	 * @return the email instance for fluent chaining
	 */
	public Email resource(String path) {
		attach(new Attachment(path), null);
		return parent;
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment is explicitly set.
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the resource to attach
	 * @return the email instance for fluent chaining
	 */
	public Email resource(String customName, String path) {
		return resource(customName, path, null);
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment is explicitly set.
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the resource to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email resource(String customName, String path, String contentType) {
		return resource(customName, new UnresolvedPath(path), contentType);
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment uses the name of the resource (extracted from the path).
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param path
	 *            the path to the resource to attach
	 * @return the email instance for fluent chaining
	 */
	public Email resource(ResourcePath path) {
		return resource(path, null);
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment uses the name of the resource (extracted from the path).
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param path
	 *            the path to the resource to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email resource(ResourcePath path, String contentType) {
		attach(new Attachment(path), contentType);
		return parent;
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment is explicitly set.
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the resource to attach
	 * @return the email instance for fluent chaining
	 */
	public Email resource(String customName, ResourcePath path) {
		return resource(customName, path, null);
	}

	/**
	 * Attach a resource loaded from a path to the email. The name of the
	 * attachment is explicitly set.
	 * 
	 * <p>
	 * The path may contain the lookup prefix (a prefix that indicates where to
	 * find the resource). For example:
	 * <ul>
	 * <li><code>"classpath:/path/to/file.pdf"</code> references a resource that
	 * is located at "path/to/file.pdf" in the classpath (from the root)</li>
	 * <li><code>"file:/path/to/file.pdf"</code> references a resource that is
	 * located at "/path/to/file.pdf" in the file system</li>
	 * </ul>
	 * By default, if the path doesn't contain any lookup prefix then the
	 * resource is loaded from the root of the classpath.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param customName
	 *            the name to use for the attachment
	 * @param path
	 *            the path to the resource to attach
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email resource(String customName, ResourcePath path, String contentType) {
		attach(new Attachment(new OverrideNameWrapper(new LookupResource(path), customName)), contentType);
		return parent;
	}

	/**
	 * Attach a resource to the email directly using the bytes. The name of the
	 * attachment must be explicitly set.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param attachmentName
	 *            the name of the attachment
	 * @param bytes
	 *            the content of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email bytes(String attachmentName, byte[] bytes) {
		return bytes(attachmentName, bytes, null);
	}

	/**
	 * Attach a resource to the email directly using the bytes. The name of the
	 * attachment must be explicitly set.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param attachmentName
	 *            the name of the attachment
	 * @param bytes
	 *            the content of the attachment
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 */
	public Email bytes(String attachmentName, byte[] bytes, String contentType) {
		attach(new Attachment(attachmentName, bytes), contentType);
		return parent;
	}

	/**
	 * Attach a resource to the email directly using the bytes read from the
	 * {@link InputStream}. The {@link InputStream} is immediately read and
	 * converted to a byte array. The name of the attachment must be explicitly
	 * set.
	 * 
	 * <p>
	 * <strong>IMPORTANT:</strong> You need to manually close the stream.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is automatically determined.
	 * 
	 * @param attachmentName
	 *            the name of the attachment
	 * @param stream
	 *            the content of the attachment
	 * @return the email instance for fluent chaining
	 * @throws IOException
	 *             when the {@link InputStream} can't be read
	 */
	public Email stream(String attachmentName, InputStream stream) throws IOException {
		return stream(attachmentName, stream, null);
	}

	/**
	 * Attach a resource to the email directly using the bytes read from the
	 * {@link InputStream}. The {@link InputStream} is immediately read and
	 * converted to a byte array. The name of the attachment must be explicitly
	 * set.
	 * 
	 * <p>
	 * <strong>IMPORTANT:</strong> You need to manually close the stream.
	 * 
	 * <p>
	 * The disposition of the attachment is set to
	 * {@link ContentDisposition#ATTACHMENT}.
	 * 
	 * <p>
	 * The Content-Type of the attachment is explicitly set.
	 * 
	 * @param attachmentName
	 *            the name of the attachment
	 * @param stream
	 *            the content of the attachment
	 * @param contentType
	 *            the Content-Type of the attachment
	 * @return the email instance for fluent chaining
	 * @throws IOException
	 *             when the {@link InputStream} can't be read
	 */
	public Email stream(String attachmentName, InputStream stream, String contentType) throws IOException {
		attach(new Attachment(attachmentName, stream), contentType);
		return parent;
	}

	/**
	 * Provides the list of attachments.
	 * 
	 * @return the list of attachments
	 */
	public List<Attachment> build() {
		return attachments;
	}

	private void attach(Attachment attachment, String contentType) {
		attachment.setContentType(contentType);
		attachments.add(attachment);
	}
}