package com.ozacc.mail.impl;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.ozacc.mail.Mail;

/**
 * XMLMailBuilder実装が継承する基底クラス。
 * 
 * @since 1.1
 * 
 * @author Tomohiro Otsuka
 * @version $Id: AbstractXMLMailBuilder.java,v 1.4.2.2 2004/11/25 08:00:49 otsuka Exp $
 */
public abstract class AbstractXMLMailBuilder {

	protected Map documentBuilderCache;

	/**
	 * コンストラクタ。
	 */
	public AbstractXMLMailBuilder() {
		documentBuilderCache = new HashMap();
	}

	/**
	 * 指定されたXMLファイルを読み込み、DOM Documentを生成します。
	 * ignoreCommentが指定されている場合は、XMLのコメントを削除しません。
	 * 
	 * @param file XMLファイル
	 * @return DOM Document
	 * @throws IOException
	 * @throws SAXException
	 */
	protected synchronized Document getDocumentFromFile(File file, boolean ignoreComment)
																							throws SAXException,
																							IOException {
		DocumentBuilder db = createDocumentBuilder(ignoreComment);
		return db.parse(file);
	}

	/**
	 * 指定されたXMLファイルを読み込み、DOM Documentを生成します。
	 * XMLのコメントや改行は削除されます。
	 * 
	 * @param file XMLファイル
	 * @return DOM Document
	 * @throws IOException
	 * @throws SAXException
	 */
	protected Document getDocumentFromFile(File file) throws SAXException, IOException {
		return getDocumentFromFile(file, true);
	}

	/**
	 * DocumentBuilderインスタンスを生成します。
	 * ignoreCommentが指定されている場合は、コメントを削除しないように設定されたDocumentBuilderを生成します。
	 * 
	 * @param ignoreComment
	 * @return DocumentBuilder
	 * @throws FactoryConfigurationError 
	 */
	protected DocumentBuilder createDocumentBuilder(boolean ignoreComment)
																			throws FactoryConfigurationError {
		Boolean dbKey = Boolean.valueOf(ignoreComment);
		DocumentBuilder db = (DocumentBuilder)documentBuilderCache.get(dbKey);
		if (db == null) {
			DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
			dbf.setIgnoringComments(ignoreComment);
			dbf.setCoalescing(ignoreComment);
			dbf.setIgnoringElementContentWhitespace(true);
			dbf.setValidating(true);
			try {
				db = dbf.newDocumentBuilder();
				db.setEntityResolver(new DTDEntityResolver());
				documentBuilderCache.put(dbKey, db);
			} catch (ParserConfigurationException e) {
				// never be thrown
				throw new RuntimeException(e);
			}
		}
		return db;
	}

	/**
	 * DocumentBuilderインスタンスを生成します。
	 * このDocumentBuilderを使用して生成されるDOM Documentでは、元のXMLデータにあるコメントは削除されます。
	 * 
	 * @return DocumentBuilder
	 * @throws FactoryConfigurationError 
	 */
	protected DocumentBuilder createDocumentBuilder() throws FactoryConfigurationError {
		return createDocumentBuilder(true);
	}

	/**
	 * 指定されたクラスパスのXMLファイルを読み込み、DOM Documentを生成します。
	 * ignoreCommentが指定されている場合は、XMLのコメントを削除しません。
	 * 
	 * @param ignoreComment
	 * @param classPath
	 * @return DOM Document
	 * @throws IOException
	 * @throws SAXException
	 */
	protected synchronized Document getDocumentFromClassPath(String classPath, boolean ignoreComment)
																										throws SAXException,
																										IOException {
		InputStream is = getClass().getResourceAsStream(classPath);
		DocumentBuilder db = createDocumentBuilder(ignoreComment);
		try {
			return db.parse(is);
		} finally {
			if (is != null) {
				is.close();
			}
		}
	}

	/**
	 * 指定されたクラスパスのXMLファイルを読み込み、DOM Documentを生成します。
	 * XMLのコメントや改行は削除されます。
	 * 
	 * @param classPath
	 * @return DOM Document
	 * @throws IOException
	 * @throws SAXException
	 */
	protected Document getDocumentFromClassPath(String classPath) throws SAXException, IOException {
		return getDocumentFromClassPath(classPath, true);
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setReplyTo(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("replyTo");
		Element replyTo = (Element)nodes.item(0);
		if (replyTo != null && replyTo.getAttribute("email").length() > 0) {
			mail.setReplyTo(replyTo.getAttribute("email"));
		}
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setText(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("body");
		Element bodyElem = (Element)nodes.item(0);
		if (bodyElem == null) {
			return;
		}
		String body = bodyElem.getFirstChild().getNodeValue();
		mail.setText(body.trim());
	}

	/**
	 * HTML本文をセット。
	 * 
	 * @param root
	 * @param mail
	 */
	protected void setHtml(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("html");
		Element htmlElem = (Element)nodes.item(0);
		if (htmlElem == null) {
			return;
		}
		String html = htmlElem.getFirstChild().getNodeValue();
		mail.setHtmlText(html.trim());
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setSubject(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("subject");
		Element subjectElem = (Element)nodes.item(0);
		if (subjectElem == null) {
			return;
		}
		String subject = subjectElem.getFirstChild().getNodeValue();
		mail.setSubject(subject.trim());
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setRecipients(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("recipients");
		Element recipientsElem = (Element)nodes.item(0);
		if (recipientsElem == null) {
			return;
		}

		NodeList recipientElemList = recipientsElem.getChildNodes();
		for (int i = 0, max = recipientElemList.getLength(); i < max; i++) {
			Node node = recipientElemList.item(i);
			if (node.getNodeType() != Node.ELEMENT_NODE) {
				continue;
			}
			Element e = (Element)node;
			if ("to".equals(e.getNodeName())) { // to
				if (e.getAttribute("email").length() > 0) {
					if (e.getAttribute("name").length() > 0) {
						mail.addTo(e.getAttribute("email"), e.getAttribute("name"));
					} else {
						mail.addTo(e.getAttribute("email"));
					}
				}
			} else if ("cc".equals(e.getNodeName())) { // cc
				if (e.getAttribute("email").length() > 0) {
					if (e.getAttribute("name").length() > 0) {
						mail.addCc(e.getAttribute("email"), e.getAttribute("name"));
					} else {
						mail.addCc(e.getAttribute("email"));
					}
				}
			} else {
				if (e.getAttribute("email").length() > 0) { // bcc
					mail.addBcc(e.getAttribute("email"));
				}
			}
		}
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setReturnPath(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("returnPath");
		Element returnPath = (Element)nodes.item(0);
		if (returnPath != null && returnPath.getAttribute("email").length() > 0) {
			mail.setReturnPath(returnPath.getAttribute("email"));
		}
	}

	/**
	 * @param root
	 * @param mail 
	 */
	protected void setFrom(Element root, Mail mail) {
		NodeList nodes = root.getElementsByTagName("from");
		Element from = (Element)nodes.item(0);
		if (from != null && from.getAttribute("email").length() > 0) {
			if (from.getAttribute("name").length() > 0) {
				mail.setFrom(from.getAttribute("email"), from.getAttribute("name"));
			} else {
				mail.setFrom(from.getAttribute("email"));
			}
		}
	}

}