/**
 * @(#)RXercesXML.java
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 *
 * RID-X : DOM Utility
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2001/06/12
 */
package jp.co.nri.rid.xml;

/* JAVA */
import	java.io.FileOutputStream;
import	java.io.StringWriter;
import	java.io.Reader;
import java.util.HashSet;

/* W3C DOM */
import	javax.xml.parsers.DocumentBuilderFactory;
import	javax.xml.parsers.DocumentBuilder;
import	org.xml.sax.InputSource;
import	org.w3c.dom.Node;
import	org.w3c.dom.NodeList;
import	org.w3c.dom.Document;
import	org.w3c.dom.Element;
import	org.w3c.dom.Text;
import	org.w3c.dom.DOMImplementation;
import	org.w3c.dom.DocumentType;

/* Xerces Serialize */
import	org.apache.xml.serialize.OutputFormat;
import	org.apache.xml.serialize.DOMSerializer;
import	org.apache.xml.serialize.XMLSerializer;

/**
 * DOM Utility for RID
 */
public class XercesXML{
	protected	Document	doc				=	null;
	protected	Element		rootelement		=	null;
	public		boolean		dtd_check		=	false;
	public		boolean		indent			=	true;
	public		String		defaultEncoding	=	"Shift_JIS";
	
	/**
	 * コンストラクタ
	 * [機能] DOMツリーを新規に作成する
	 *         ・docフィールドに新規に作成したDOMツリーのDocumentを設定
	 *         ・rootフィールドに新規に作成したDOMツリーのDocumentの
	 *           ルートエレメントを設定
	 * @param String RootElementName ルートエレメント名("ProjrctUnit"等)
	 * @param String SystemID        システムID("site.dtd"等)
	 */
	public XercesXML(String RootElementName ,String SystemID)
		throws
			 javax.xml.parsers.ParserConfigurationException
	{
		// ドキュメントビルダインスタンス化
		DocumentBuilderFactory	factory	=	DocumentBuilderFactory.newInstance();
		DocumentBuilder			builder	=	factory.newDocumentBuilder();

		// ドキュメントタイプインスタンス化
		DOMImplementation	impl	=	builder.getDOMImplementation();
		DocumentType		doctype	=	impl.createDocumentType(RootElementName ,null, SystemID);
		
		// ドキュメントインスタンス化
		doc	=	impl.createDocument(null ,RootElementName ,doctype);
		jp.co.iti.fagot.util.ZDebug.trace("doc class:"+doc.getClass());

		// ルートエレメント取得
		rootelement	=	doc.getDocumentElement();
		jp.co.iti.fagot.util.ZDebug.trace("root class:"+rootelement.getClass());
	}
	
	/**
	 * コンストラクタ
	 * [機能] DOMツリーを新規に作成する
	 *         ・docフィールドに新規に作成したDOMツリーのDocumentを設定
	 *         ・rootフィールドに新規に作成したDOMツリーのDocumentの
	 *           ルートエレメントを設定
	 * @param String RootElementName ルートエレメント名("ProjrctUnit"等)
	 * @param String PublicID        パブリックID
	 * @param String SystemID        システムID("site.dtd"等)
	 */
	public XercesXML(String RootElementName ,String PublicID ,String SystemID)
		throws
			 javax.xml.parsers.ParserConfigurationException
	{
		// ドキュメントビルダインスタンス化
		DocumentBuilderFactory	factory	=	DocumentBuilderFactory.newInstance();
		DocumentBuilder			builder	=	factory.newDocumentBuilder();

		// ドキュメントタイプインスタンス化
		DOMImplementation	impl	=	builder.getDOMImplementation();
		DocumentType		doctype	=	impl.createDocumentType(RootElementName ,PublicID, SystemID);
		
		// ドキュメントインスタンス化
		doc	=	impl.createDocument(null ,RootElementName ,doctype);

		// ルートエレメント取得
		rootelement	=	doc.getDocumentElement();
	}
	
	/**
	 * コンストラクタ
	 * [機能] xmluriのXMLファイルからDOMツリーを作成する
	 *         ・docフィールドに作成したDOMツリーのDocumentを設定
	 *         ・rootフィールドに作成したDOMツリーのDocumentの
	 *           ルートエレメントを設定
	 * @param String xmluri 読み込むXMLファイルのURI
	 */
	public	XercesXML(String	xmluri)
		throws
			 javax.xml.parsers.ParserConfigurationException
			,org.xml.sax.SAXException
			,java.io.IOException
	{
		// ドキュメントビルダインスタンス化
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();	// ﾋﾞﾙﾀﾞﾌｧｸﾄﾘ生成
		factory.setIgnoringElementContentWhitespace(true);	// 不必要なﾎﾜｲﾄｽﾍﾟｰｽは無視ｵﾌﾟｼｮﾝ(空ｴﾚﾒﾝﾄﾉｰﾄﾞを作らない)
		factory.setValidating(dtd_check);						// XML検証ｵﾌﾟｼｮﾝ
		DocumentBuilder builder = factory.newDocumentBuilder();	// ﾄﾞｷｭﾒﾝﾄﾋﾞﾙﾀﾞ生成

		// ドキュメントインスタンス化
		doc	=	builder.parse(xmluri);	// ﾊﾟｰｽ
		jp.co.iti.fagot.util.ZDebug.trace("doc class:"+doc.getClass());

		jp.co.iti.fagot.util.ZDebug.trace("doctype:"+doc.getClass().toString());
		// ルートエレメント取得
		rootelement	=	doc.getDocumentElement();
		jp.co.iti.fagot.util.ZDebug.trace("root class:"+rootelement.getClass());
	}
	
	/**
	 * コンストラクタ
	 * [機能] ReaderからDOMツリーを作成する
	 *         ・docフィールドに作成したDOMツリーのDocumentを設定
	 *         ・rootフィールドに作成したDOMツリーのDocumentの
	 *           ルートエレメントを設定
	 * @param Reader r 読み込むXMLのReader(StringReader等)
	 */
	public	XercesXML(Reader r)
		throws
			 javax.xml.parsers.ParserConfigurationException
			,org.xml.sax.SAXException
			,java.io.IOException
	{
		// ドキュメントビルダインスタンス化
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();	// ﾋﾞﾙﾀﾞﾌｧｸﾄﾘ生成
		factory.setIgnoringElementContentWhitespace(true);	// 不必要なﾎﾜｲﾄｽﾍﾟｰｽは無視ｵﾌﾟｼｮﾝ(空ｴﾚﾒﾝﾄﾉｰﾄﾞを作らない)
		factory.setValidating(dtd_check);						// XML検証ｵﾌﾟｼｮﾝ
		DocumentBuilder builder = factory.newDocumentBuilder();	// ﾄﾞｷｭﾒﾝﾄﾋﾞﾙﾀﾞ生成

		// ドキュメントインスタンス化
		doc	=	builder.parse(new InputSource(r));	// ﾊﾟｰｽ

		// ルートエレメント取得
		rootelement	=	doc.getDocumentElement();
	}
	
	/**
	 * writetoメソッド <public>
	 * [機能] DOMツリーを新規ファイルに書き込む
	 * @ret   void
	 * @param String filename 新規に作成するXMLファイル名
	 */
	public	void	writeto(String filename ,String encode)
		throws
			 java.io.FileNotFoundException
			,java.io.IOException
	{
		OutputFormat	fm	=	new OutputFormat(doc ,encode ,indent);
		
		DOMSerializer	ser	=
		new XMLSerializer(new FileOutputStream(filename),fm).asDOMSerializer();
		
		ser.serialize(doc);
	}
	
	public	void	writeto(String filename)
		throws
			 java.io.FileNotFoundException
			,java.io.IOException
	{
		writeto(filename ,defaultEncoding);
	}
	
	/**
	 * getStringメソッド <public>
	 * [機能] XMLファイルへの出力イメージをそのまま文字列化する。
	 * @ret   String 文字列化したXMLデータ
	 * @param なし
	 */
	public	String	getString()
		throws	java.io.IOException
	{
		OutputFormat	fm	=	new OutputFormat(doc ,defaultEncoding ,indent);
		
		StringWriter	sw	=	new StringWriter();
		DOMSerializer	ser	=
		new XMLSerializer(sw ,fm).asDOMSerializer();
		
		ser.serialize(doc);
		
		String	s	=	new String(sw.toString());
		sw.close();
		
		return(s);
	}
	
	/**
	 * documentメソッド <public>
	 * [機能] DOMツリーのDocumentノードを求める
	 * @ret   Document DOMツリーのDocumentノード
	 */
	public	Document	document(){
		return(doc);
	}
	
	/**
	 * RootElementメソッド <public>
	 * [機能] DOMツリーのルートエレメントを求める
	 * @ret   Element DOMツリーのルートエレメント
	 */
	public	Element	RootElement(){
		return(rootelement);
	}
	
	/**
	 * createTEXTonELEMENTメソッド <public>
	 * [機能] テキストノードを持つエレメントを生成
	 * @ret   Element 生成されたエレメント
	 * @param String text        テキスト文字列
	 * @param String elementname エレメント名
	 */
	public	Element	createTEXTonELEMENT(String text ,String elementname){
		return(createTEXTonELEMENT(text ,elementname ,doc));
	}
	
	/**
	 * createTEXTonELEMENTメソッド <public static>
	 * [機能] テキストノードを持つエレメントを生成
	 * @ret   Element 生成されたエレメント
	 * @param String   text        テキスト文字列
	 * @param String   elementname エレメント名
	 * @param Document document    エレメントを生成するドキュメントノード
	 */
	public	static	Element	createTEXTonELEMENT(String text ,String elementname ,Document document){
		Element	e	=	document.createElement(elementname);
		Text	t	=	document.createTextNode(text);
		e.appendChild(t);
		
		return(e);
	}
	
	/**
	 * createELEMENTonELEMENTメソッド <public>
	 * [機能] エレメントノードを持つエレメントを生成
	 * @ret   Element 生成されたエレメント(エレメントB)
	 * @param String elementnameA エレメントBに作成するエレメント名
	 * @param String elementnameB 親エレメント名
	 */
	public	Element	createELEMENTonELEMENT(String elementnameA ,String elementnameB){
		return(createELEMENTonELEMENT(elementnameA ,elementnameB ,doc));
	}
	
	/**
	 * createELEMENTonELEMENTメソッド <public static>
	 * [機能] エレメントノードを持つエレメントを生成
	 * @ret   Element 生成されたエレメント(エレメントB)
	 * @param String elementnameA エレメントBに作成するエレメント名
	 * @param String elementnameB 親エレメント名
	 * @param Document document   エレメントを生成するドキュメントノード
	 */
	public	static	Element	createELEMENTonELEMENT(String elementnameA ,String elementnameB ,Document document){
		Element	B	=	document.createElement(elementnameB);
		Element	A	=	document.createElement(elementnameA);
		B.appendChild(A);
		
		return(B);
	}
	
	/**
	 * getTEXTonELEMENTメソッド <public static>
	 * [機能] タグ名で子エレメントを得る
	 * @ret   Element 獲得したエレメント
	 * @param Element parent 親エレメント
	 * @param String  tag    タグ名
	 */
	public	static	String	getTEXTonELEMENT(Element e){
		NodeList	nl = e.getChildNodes();
		
		for(int i = 0;i < nl.getLength();i++){
			Node n = nl.item(i);
			if(Node.TEXT_NODE == n.getNodeType()){
				String s = n.getNodeValue().trim();
				if(s.length() > 0)	return(s);
				return(null);
			}
		}
		
		return(null);
	}
	
	/**
	 * getChildElementByTagNameメソッド <public static>
	 * [機能] タグ名で子エレメントを得る
	 * @ret   Element 獲得したエレメント
	 * @param Element parent 親エレメント
	 * @param String  tag    タグ名
	 */
	public	static	Element	getChildElementByTagName(Element parent ,String tag){
		NodeList	nl = parent.getChildNodes();
		
		for(int i = 0;i < nl.getLength();i++){
			Node n = nl.item(i);
			if(Node.ELEMENT_NODE != n.getNodeType())	continue;
			if(tag.equalsIgnoreCase(n.getNodeName()))	return((Element)n);
		}
		
		return(null);
	}
	
	/**
	 * getChildElementTextByTagNameメソッド <public static>
	 * [機能] タグ名で子エレメントのPCDATAを得る
	 * @ret   String 獲得したPCDATA
	 * @param Element parent 親エレメント
	 * @param String  tag    タグ名
	 */
	public	static	String	getChildElementTextByTagName(Element parent ,String tag){
		Element e = getChildElementByTagName(parent ,tag);
		if(null == e)	return(null);
		return(getTEXTonELEMENT(e));
	}

	
	/**
	 * getChildElementByTagNameメソッド <public static>
	 * [機能] タグ名で子エレメントを得る
	 * @ret   Element 獲得したエレメント
	 * @param Element parent 親エレメント
	 * @param String  tag    タグ名
	 */
	public	static	Element	getChildElementByAttribute(Element parent ,String tag ,String attrname ,String attrstr){
		NodeList	nl = parent.getChildNodes();
		
		for(int i = 0;i < nl.getLength();i++){
			Node n = nl.item(i);
			if(Node.ELEMENT_NODE != n.getNodeType())	continue;
			if(!tag.equalsIgnoreCase(n.getNodeName()))	continue;
			
			String s = ((Element)n).getAttribute(attrname);
			if(null == s)	continue;
			if(s.equalsIgnoreCase(attrstr))	return((Element)n);
		}
		
		return(null);
	}
	
	public	static	String[]	toHashElementNames(NodeList list){
/*		
		if(null == list)	return(null);
		
		int mx = list.getLength();
		
		HashSet hs = new HashSet();
		for(int i = 0;i < mx;i++){
			Node n = list.item(i);
			if(Node.ELEMENT_NODE != n.getNodeType())	continue;
			hs.add(n.getNodeName());
		}
*/		
		HashSet hs = new HashSet();
		addHashSet(hs ,list ,null ,false);
		if(null==hs)		return(null);
		if(hs.isEmpty())	return(null);
		
/*
		Object[] objs = hs.toArray();
		String[] names = new String[objs.length];
		for(int i=0;i<names.length;i++)	names[i] = (String)objs[i];
*/		
		return(toStringArray(hs.toArray()));
	}
	
	public	static	String[]	toStringArray(Object[] objs){
		if(null==objs)	return null;
		String[] strs = new String[objs.length];
		int mx = strs.length;
		for(int i=0;i<mx;i++) strs[i] = objs[i].toString();
		
		return(strs);
	}
	
	public	static	void	addHashSet(HashSet hs ,NodeList list ,String prefix ,boolean flag){
		String SEPARATOR = "/";
		if(null == list)	return;
		
		int mx = list.getLength();
		
		for(int i = 0;i < mx;i++){
			Node n = list.item(i);
			if(Node.ELEMENT_NODE != n.getNodeType())	continue;
			String name = n.getNodeName();
			if(null!=prefix)	name = prefix + name;
			hs.add(name);
			if(flag)	addHashSet(hs ,n.getChildNodes() ,name+SEPARATOR ,flag);
		}
		
		return;
	}
	
	public	static	Element	getElementByTagName(Element e ,String name){
		if(null == e)		return(null);
		if(null == name)	return(null);
		NodeList nl = e.getElementsByTagName(name);
		
		if(null == nl)			return(null);
		if(0 >= nl.getLength())	return(null);
		return	(Element)nl.item(0);
	}
}
