package com.ozacc.mail.impl;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.FactoryConfigurationError;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;

import com.ozacc.mail.Mail;
import com.ozacc.mail.MailBuildException;
import com.ozacc.mail.MultipleMailBuilder;

/**
 * メールデータのXMLファイルからMailインスタンスを生成するクラス。
 * <p>
 * ソースXMLを読み込む際に、DTDバリデーションが実行されますので妥当なXMLデータ(Valid XML Document)でなければいけません。
 * メールデータXMLのDTDは、<a href="http://www.ozacc.com/library/dtd/ozacc-mail.dtd">http://www.ozacc.com/library/dtd/ozacc-mail.dtd</a>を参照。
 * 
 * @since 1.0.1
 * @author Tomohiro Otsuka
 * @version $Id: XMLMailBuilderImpl.java,v 1.5.2.1 2005/01/21 22:16:31 otsuka Exp $
 */
public class XMLMailBuilderImpl extends AbstractXMLMailBuilder implements MultipleMailBuilder {

	private static Log log = LogFactory.getLog(XMLMailBuilderImpl.class);

	/**
	 * コンストラクタ。
	 */
	public XMLMailBuilderImpl() {
		super();
	}

	/**
	 * @see com.ozacc.mail.MailBuilder#buildMail(java.lang.String)
	 */
	public Mail buildMail(String classPath) throws MailBuildException {
		Document doc = retrieveDocument(classPath);
		return buildMail(doc.getDocumentElement());
	}

	/**
	 * @see com.ozacc.mail.MailBuilder#buildMail(java.io.File)
	 */
	public Mail buildMail(File file) throws MailBuildException {
		Document doc = retrieveDocument(file);
		return buildMail(doc.getDocumentElement());
	}

	/**
	 * @param classPath
	 * @return 
	 * @throws MailBuildException
	 */
	private Document retrieveDocument(String classPath) throws MailBuildException {
		try {
			return getDocumentFromClassPath(classPath);
		} catch (SAXException e) {
			throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
		} catch (IOException e) {
			throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
		}
	}

	/**
	 * @param file
	 * @return 
	 * @throws MailBuildException
	 */
	private Document retrieveDocument(File file) throws MailBuildException {
		try {
			return getDocumentFromFile(file);
		} catch (SAXException e) {
			throw new MailBuildException("XMLのパースに失敗しました。" + e.getMessage(), e);
		} catch (IOException e) {
			throw new MailBuildException("XMLファイルの読み込みに失敗しました。", e);
		}
	}

	/**
	 * 指定されたXMLのmail要素からMailインスタンスを生成します。
	 * 
	 * @param root メールデータのmail要素
	 * @return 生成されたMailインスタンス
	 */
	protected Mail buildMail(Element root) {
		Mail mail = new Mail();
		setReturnPath(root, mail);
		setFrom(root, mail);
		setRecipients(root, mail);
		setReplyTo(root, mail);
		setSubject(root, mail);
		setText(root, mail);
		setHtml(root, mail);
		return mail;
	}

	/**
	 * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.lang.String, java.lang.String)
	 */
	public Mail buildMail(String classPath, String mailId) throws MailBuildException {
		if (mailId == null || "".equals(mailId)) {
			throw new IllegalArgumentException("メールIDが指定されていません。");
		}
		Document doc = retrieveDocument(classPath);
		if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {
			throw new MailBuildException("指定されたクラスパスのXMLはシングルメールテンプレートです。[classPath='" + classPath
					+ "']");
		}
		Element mailElem = doc.getElementById(mailId);
		return buildMail(mailElem);
	}

	/**
	 * @see com.ozacc.mail.MultipleMailBuilder#buildMail(java.io.File, java.lang.String)
	 */
	public Mail buildMail(File file, String mailId) throws MailBuildException {
		if (mailId == null || "".equals(mailId)) {
			throw new IllegalArgumentException("メールIDが指定されていません。");
		}
		Document doc = retrieveDocument(file);
		if (Mail.DOCTYPE_PUBLIC.equals(doc.getDoctype().getPublicId())) {
			throw new MailBuildException("指定されたファイルのXMLはシングルメールテンプレートです。[filePath='"
					+ file.getAbsolutePath() + "']");
		}
		return buildMail(doc, mailId);
	}

	/**
	 * マルチプルメールテンプレートのXMLドキュメント上の指定されたIDが示すメールテンプレートからMailインスタンスを生成して返します。
	 * 
	 * @param doc
	 * @param mailId
	 * @return 生成されたMailインスタンス
	 * @throws FactoryConfigurationError 
	 */
	protected Mail buildMail(Document doc, String mailId) throws FactoryConfigurationError {
		Element mailElem = doc.getElementById(mailId);
		if (mailElem == null) {
			throw new MailBuildException("指定されたID[" + mailId + "]のメールデータは見つかりませんでした。");
		}
		log.debug(mailElem);
		return buildMail(mailElem);
	}

}