/*
このファイルの中身は、NTT オープンソースライセンス　バージョン 1.0（「本
契約」という）の適用を受けます。
本契約を遵守しない限り、このファイルを使用してはなりません。
本契約のコピーは、次のＵＲＬから入手できます。
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

本契約に基づき頒布されるソフトウェアは、現状のまま、明示又は黙示のあらゆ
る種類の保証なしで、頒布されます。本契約に基づく権利及び制限を律する特定
の文言については、本契約を参照してください。

「オリジナルコード」は、 NTT Cyber Space Laboratories Code です。 
「オリジナルコード」の「初期開発者」は、日本電信電話株式会社です。  
日本電信電話株式会社により創作された部分は、Copyright (C) 2004 
日本電信電話株式会社 です。
全ての権利が留保されます。 
「コントリビュータ」：_____________________________________ 


The contents of this file are subject to the NTT Opensource License
Version 1.0 (the License); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
【配布サイトのURL】 http://www.oss.ecl.ntt.co.jp/lms/

Software distributed under the License is distributed on an AS IS
basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
License for the specific language governing rights and limitations
under the License.

The Original Code is NTT Cyber Space Laboratories Code .

The Initial Developer of the Original Code is NIPPON TELEGRAPH AND 
TELEPHONE CORPORATION.
Portions created by the NIPPON TELEGRAPH AND TELEPHONE CORPORATION 
are Copyright (C) 2004 NIPPON TELEGRAPH AND TELEPHONE CORPORATION. 
All Rights Reserved.

Contributor(s) ______________________________________.
*/

//
//	XMLUtil
//	変更履歴
//		2004.02.01	新規作成
//

package jp.co.ntt.lms.lo.scorm.util;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;

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

/**
 *	XMLUtil クラス<br>
 *	XMLのユーティリティクラス<br>
 */
public class XMLUtil {

	/**
	 * DocumentBuilderを取得する
	 *
	 * @param	namespaceAware	名前空間を有効にするかどうかを指定する
	 */
	public static DocumentBuilder newDocumentBuilder(boolean namespaceAware) throws ParserConfigurationException {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		dbf.setNamespaceAware(namespaceAware);

		return dbf.newDocumentBuilder();
	}

	/**
	 * DOM Documentを作成する
	 *
	 * @param	is	InputStreamを指定する
	 * @return	作成したDOM Documentを返却する
	 */
	public static Document createDocument(InputStream is, boolean namespaceAware)
		throws IOException, ParserConfigurationException, SAXException {

		return newDocumentBuilder(namespaceAware).parse(is);
	}

	/**
	 * DOM Documentを作成する
	 *
	 * @param	rootNodeName	ルートノードの名称を指定する
	 * @param	namespaceAware	名前空間を有効にするかどうかを指定する
	 * @return	作成したDOM Documentを返却する
	 */
	public static Document createDocument(String rootNodeName, boolean namespaceAware)
		throws ParserConfigurationException {

		Document doc = newDocumentBuilder(namespaceAware).newDocument();
		doc.appendChild(doc.createElement(rootNodeName));
		return doc;
	}

	/**
	 * DOM Documentを作成する
	 *
	 * @param	rootNode		ルートノードを指定する
	 * @param	namespaceAware	名前空間を有効にするかどうかを指定する
	 * @return	作成したDOM Documentを返却する
	 */
	public static Document createDocument(Node rootNode, boolean namespaceAware)
		throws ParserConfigurationException {

		Document doc = newDocumentBuilder(namespaceAware).newDocument();
		doc.appendChild(doc.importNode(rootNode, true));
		return doc;
	}

	/**
	 * Textノードを作成する
	 *
	 * @param	doc		DOM Documentを指定する
	 * @param	text	作成するTextノードに設定するテキストを指定する。
	 *					テキストが無い場合はnullもしくは""(空文字列)を指定する
	 * @return	作成したTextノードを返却する
	 */
	public static Text createTextNode(Document doc, String text) {
		return doc.createTextNode((text == null) ? "" : text);
	}

	/**
	 * Elementノードを作成する
	 *
	 * @param	doc			DOM Documentを指定する
	 * @param	nodeName	作成するElementノードのノード名称を指定する
	 * @param	text		作成するElementノードに設定するテキストを指定する。
	 *						テキストが無い場合はnullもしくは""(空文字列)を指定する
	 * @return	作成したElementノードを返却する
	 */
	public static Element createElement(Document doc, String nodeName, String text) {

		Element element = doc.createElement(nodeName);
		element.appendChild(createTextNode(doc, text));

		return element;
	}

	/**
	 * コンテナのElementノードを作成し、子Elementを追加する
	 *
	 * @param	doc					DOM Documentを指定する
	 * @param	containerNodeName	作成するコンテナノードのノード名称を指定する
	 * @param	children			追加するElementノードの配列を指定する
	 * @return	作成したコンテナのElementノードを返却する
	 */
	public static Element createElement(Document doc, String containerNodeName, Element[] children) {

		Element element = doc.createElement(containerNodeName);

		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				element.appendChild(children[i]);
			}
		}

		return element;
	}

	/**
	 * Elementノードを作成し、指定されたノードへ追加する
	 *
	 * @param	doc			DOM Documentを指定する
	 * @param	parent		追加先のノードを指定する
	 * @param	nodeName	追加するノードのノード名称を指定する
	 * @param	text		追加するノードに設定するTextノードのテキストを指定する。
	 *						テキストが無い場合はnullもしくは""(空文字列)を指定する
	 * @return	(作成し)追加したElementノードを返却する
	 */
	public static Element append(Document doc, Element parent, String nodeName, String text) {

		Element element = createElement(doc, nodeName, text);
		parent.appendChild(element);

		return element;
	}

	/**
	 * コンテナのElementノードを作成し、子Elementを追加し、parentで
	 * 指定されたノードへコンテナノードを追加する
	 *
	 * @param	doc					DOM Documentを指定する
	 * @param	parent				コンテナノードの追加先のノードを指定する
	 * @param	containerNodeName	作成するコンテナノードのノード名称を指定する
	 * @param	children			追加するElementノードの配列を指定する
	 * @return	作成したコンテナのElementノードを返却する
	 */
	public static Element append(Document doc, Element parent, String containerNodeName, Element[] children) {

		Element element = doc.createElement(containerNodeName);

		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				element.appendChild(children[i]);
			}
		}

		parent.appendChild(element);

		return element;
	}

	/**
	 * parentで指定されたノードへ(子)Elementを追加する
	 *
	 * @param	doc		DOM Documentを指定する
	 * @param	parent	コンテナノードの追加先のノードを指定する
	 * @param	child	追加するElementノードを指定する
	 * @return	Elementが追加されたElementノード(parent)を返却する
	 */
	public static Element append(Document doc, Element parent, Element child) {
		parent.appendChild(child);
		return parent;
	}

	/**
	 * parentで指定されたノードへ(子)Elementを追加する
	 *
	 * @param	doc			DOM Documentを指定する
	 * @param	parent		コンテナノードの追加先のノードを指定する
	 * @param	children	追加するElementノードの配列を指定する
	 * @return	Elementが追加されたElementノード(parent)を返却する
	 */
	public static Element append(Document doc, Element parent, Element[] children) {

		for (int i = 0; i < children.length; i++) {
			parent.appendChild(children[i]);
		}

		return parent;
	}

	/**
	 * parentで指定されたノードへ(子)Elementを追加する
	 *
	 * @param	doc			DOM Documentを指定する
	 * @param	parent		コンテナノードの追加先のノードを指定する
	 * @param	childName	追加するElementノードの名称を指定する
	 * @return	追加した(子)Elementを返却する
	 */
	public static Element append(Document doc, Element parent, String childName) {

		Element child = doc.createElement(childName);
		parent.appendChild(child);
		return child;
	}

	/**
	 * parentで指定されたノードへ(子)Elementを追加する
	 *
	 * @param	doc		DOM Documentを指定する
	 * @param	parent	コンテナノードの追加先のノードを指定する
	 * @param	child	追加するElementノードを指定する
	 * @return	childを返却する
	 */
	public static Element appendChild(Document doc, Element parent, Element child) {

		parent.appendChild(child);
		return child;
	}

	/**
	 * 属性ノードを作成する
	 *
	 * @param	doc				DOM Documentを指定する
	 * @param	attributeName	属性ノードの名称を指定する
	 * @param	value			属性ノードの値を指定する
	 * @return	作成した属性ノードを返却する
	 */
	public static Attr createAttribute(Document doc, String attributeName, String value) {

		Attr attribute = doc.createAttribute(attributeName);
		attribute.setValue(value);

		return attribute;
	}

	/**
	 * 指定されたElementノードに属性ノードの追加を行う
	 *
	 * @param	doc				DOM Documentを指定する
	 * @param	parent			属性ノードを追加するElementノードを指定する
	 * @param	attributeName	属性ノードの名称を指定する
	 * @param	value			属性ノードの値を指定する
	 * @return	属性が追加されたElementノード(parent)を返却する
	 */
	public static Element insertAttribute(Document doc, Element parent, String attributeName, String value) {

		parent.setAttributeNode(createAttribute(doc, attributeName, value));

		return parent;
	}

	/**
	 * 指定されたElementノードに属性ノードの追加を行う
	 *
	 * @param	doc				DOM Documentを指定する
	 * @param	parent			属性ノードを追加するElementノードを指定する
	 * @param	attributes		追加する属性ノードの配列を指定する
	 * @return	属性が追加されたElementノード(parent)を返却する
	 */
	public static Element insertAttribute(Document doc, Element parent, Attr[] attributes) {

		if (attributes != null) {
			for (int i = 0; i < attributes.length; i++) {
				parent.setAttributeNode(attributes[i]);
			}
		}

		return parent;
	}

	/**
	 * 指定されたElementノードに属性ノードの追加を行う
	 *
	 * @param	doc				DOM Documentを指定する
	 * @param	parentName		属性ノードを追加するElementノードを名称を指定する
	 * @param	attributes		追加する属性ノードの配列を指定する
	 * @return	属性が追加されたElementノード(parent)を返却する
	 */
	public static Element insertAttribute(Document doc, String parentName, Attr[] attributes) {

		Element parent = doc.createElement(parentName);

		if (attributes != null) {
			for (int i = 0; i < attributes.length; i++) {
				parent.setAttributeNode(attributes[i]);
			}
		}

		return parent;
	}

	/**
	 * 指定されたノードをoutで指定されたResultへ出力する
	 *
	 * @param	node				出力するノードを指定する
	 * @param	omitXMLDeclaration	XML宣言を付与するかどうか指定する
	 *								省略する場合trueを、省略しない場合はfalseを指定する
	 * @param	encoding			文字エンコーディングを指定する。nullを指定した場合は省略される。
	 *								(UTF-8でエンコーディングが行われる)
	 * @param	out					出力先をResultで指定する
	 */
	public static void output(Node node, boolean omitXMLDeclaration, String encoding, Result out)
		throws TransformerConfigurationException, TransformerException {

		Transformer transformer = TransformerFactory.newInstance().newTransformer();

		Properties prop = new Properties();

		prop.setProperty(OutputKeys.METHOD, "xml");
		prop.setProperty(OutputKeys.VERSION, "1.0");
		if (encoding != null) {
			prop.setProperty(OutputKeys.ENCODING, encoding);
		}
		prop.setProperty(OutputKeys.OMIT_XML_DECLARATION, (omitXMLDeclaration) ? "yes" : "no");
		prop.setProperty(OutputKeys.INDENT, "yes");
		transformer.setOutputProperties(prop);

		transformer.transform(new DOMSource(node), out);
	}

	/**
	 * 指定されたXMLファイルをoutで指定されたResultへ出力する
	 *
	 * @param	file				出力するXMLファイルを指定する
	 * @param	omitXMLDeclaration	XML宣言を付与するかどうか指定する
	 *								省略する場合trueを、省略しない場合はfalseを指定する
	 * @param	encoding			文字エンコーディングを指定する。nullを指定した場合は省略される。
	 *								(UTF-8でエンコーディングが行われる)
	 * @param	out					出力先をResultで指定する
	 */
	public static void output(File file, boolean omitXMLDeclaration, String encoding, Result out)
		throws TransformerConfigurationException, TransformerException {

		Transformer transformer = TransformerFactory.newInstance().newTransformer();

		Properties prop = new Properties();

		prop.setProperty(OutputKeys.METHOD, "xml");
		prop.setProperty(OutputKeys.VERSION, "1.0");
		if (encoding != null) {
			prop.setProperty(OutputKeys.ENCODING, encoding);
		}
		prop.setProperty(OutputKeys.OMIT_XML_DECLARATION, (omitXMLDeclaration) ? "yes" : "no");
		prop.setProperty(OutputKeys.INDENT, "yes");
		transformer.setOutputProperties(prop);

		transformer.transform(new StreamSource(file), out);
	}
}
