/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JFUXPathManager
*   ソースファイル名：JFUXPathManager.java
*   作成者          ：富士通
*   日付            ：2011年06月02日
*＜機能概要＞
*   XPath操作manager部品です。
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v1.00.00    2011/06/02   FJ）和田    新規作成
*   v15.00.00   2015/10/14   FJ）西川   【OM-2015-0002416】XML情報の欠落事象対応
*   v47.00.00   2019/09/26   FJ）大島   【ANK-3715-00-00】提供条件リリース対応
*
**********************************************************************/
package eo.web.webview.common;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

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.fujitsu.futurity.common.JSYLogBase;
import com.fujitsu.futurity.common.JSYwebLog;

/**
 * XPathを操作するmanager部品です。
 * <br>
 * @author 富士通
 */
public class JFUXPathManager
{

	/** W3CXMLSchemeのネームスペース */
	private static final String W3C_XML_SCHEMA_NS_URI = "http://www.w3.org/2001/XMLSchema";

	/** XPath評価環境および式へのアクセスを提供するインスタンス */
	private XPath xpath = null;

	/** XML文書全体を表すインスタンス */
	private Document document = null;

	/** XML文書全体のサイズチェック false：未実施、trueチェック済 */
	private boolean checkXmlSize = false;

	/**
	 * XPath操作manager部品を生成します。
	 * <br>
	 */
	protected JFUXPathManager()
	{
		super();
	}

	/**
	 * XPath操作manager部品を生成します。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @throws Exception XML解析時に発生する例外
	 */
	public JFUXPathManager(String xmlFilePath) throws Exception
	{
		analyzeXml(xmlFilePath);
	}

	/**
	 * XPath操作manager部品を生成します。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @param schemaFilePath XML Schemaのパス
	 * @throws Exception XML解析時に発生する例外
	 */
	public JFUXPathManager(String xmlFilePath, String schemaFilePath) throws Exception
	{
		analyzeXml(xmlFilePath, schemaFilePath);
	}

	/**
	 * XPath操作manager部品を生成します。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @param doc XML文書全体
	 * @throws Exception XML解析時に発生する例外
	 */
	public JFUXPathManager(String xmlFilePath, Document doc) throws Exception
	{
		setDocument(doc);
		analyzeXml(xmlFilePath);
	}

	/**
	 * 引数のXMLのパスで指定したXMLを読込みます。
	 * <br>
	 * XMLを読込む際に、引数のXMLのパスで指定されたXMLSchemaを使用し構文チェックを行います。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @param schemaFilePath XML Schemaのパス
	 * @throws Exception XML解析時に発生する例外
	 */
	protected void analyzeXml(String xmlFilePath, String schemaFilePath) throws Exception
	{
		analyzeXml(xmlFilePath);
		try
		{
			validateSchema(schemaFilePath);
		}
		catch (SAXException e)
		{
			throw new Exception(e);
		}
	}

	/**
	 * 引数のXMLのパスで指定したXMLを読込みます。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @throws Exception XML解析時に発生する例外
	 */
	protected void analyzeXml(String xmlFilePath) throws Exception
	{
		try
		{
			buildDocument(xmlFilePath);
		}
		catch (SAXException e)
		{
			throw new Exception(e);
		}
	}

	/**
	 * XMLを読み込みます。
	 * <br>
	 * @param xmlFilePath XMLのパス
	 * @throws Exception 
	 */
	protected void buildDocument(String xmlFilePath) throws Exception
	{
		DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
		Document docobj = builder.parse(new File(xmlFilePath));

		XPathFactory factory = XPathFactory.newInstance();
		XPath xpathobj = factory.newXPath();
		setCheckXmlSize(checkXmlSize(docobj));
		setDocument(docobj);
		setXpath(xpathobj);
	}

	/**
	 * SchemaをつかってXMLを検証します。
	 * <br>
	 * @param schemaFilePath XML Schemaのパス
	 * @throws SAXException 検証時に発生する例外
	 * @throws IOException 検証時に発生する例外
	 */
	protected void validateSchema(String schemaFilePath) throws SAXException, IOException
	{
		SchemaFactory factory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI);
		Source schemaFile = new StreamSource(new File(schemaFilePath));
		Schema schema = factory.newSchema(schemaFile);
		Validator validator = schema.newValidator();
		validator.validate(new DOMSource(document));
	}

	/**
	 * 引数のクエリを使用し指定した要素を文字列として取得し返却します。
	 * <br>
	 * @param query クエリ
	 * @return 評価結果
	 * @throws Exception XML解析時に発生する例外
	 */
	public String getItem(String query) throws Exception
	{
		try
		{
			String s = (String)getXpath().evaluate(query, getDocument(), XPathConstants.STRING);
			s = s.trim();
			return s;
		}
		catch (XPathExpressionException e)
		{
			throw new Exception(e);
		}
	}

	/**
	 * 引数のクエリを使用し指定した要素が存在するかを確認し返却します。
	 * <br>
	 * @param query クエリ
	 * @return 評価結果
	 * @throws Exception XML解析時に発生する例外
	 */
	public boolean isElement(String query) throws Exception
	{
		try
		{
			Boolean bool = (Boolean)getXpath().evaluate(query, getDocument(), XPathConstants.BOOLEAN);
			return bool.booleanValue();
		}
		catch (XPathExpressionException e)
		{
			throw new Exception(e);
		}
	}

	/**
	 * 引数のクエリを使用しNodeLisを取得し返却します。
	 * <br>
	 * @param query クエリ
	 * @return 評価結果
	 * @throws Exception XML解析時に発生する例外
	 */
	protected NodeList getItemAsNodeList(String query) throws Exception
	{
		try
		{
			NodeList list = (NodeList)getXpath().evaluate(query, getDocument(), XPathConstants.NODESET);
			return list;
		}
		catch (XPathExpressionException e)
		{
			throw new Exception(e);
		}
	}

	/**
	 * 引数のクエリを使い取得したNodeListの属性値をキーに、要素をバリューとして保持したMapを返却します。
	 * <br>
	 * @param query クエリ
	 * @param attrName 属性名
	 * @return Mapオブジェクト
	 * @throws Exception XML解析時に発生する例外
	 */
	public Map<String, String> getItemsAsMap(String query, String attrName) throws Exception
	{
		NodeList nodeList = getItemAsNodeList(query);
		Map<String, String> map = new HashMap<String, String>();
		for (int i = 0; i < nodeList.getLength(); i++)
		{
			Node node = nodeList.item(i);
			Node nodeTarget = node.getAttributes().getNamedItem(attrName);
			if (nodeTarget != null)
			{
				String key = nodeTarget.getNodeValue();
				String value = node.getTextContent().trim();
				map.put(key, value);
			}
		}
		return map;
	}

	/**
	 * 引数のクエリを使い取得したNodeListの属性名の値を返却します。
	 * <br>
	 * @param query クエリ
	 * @param attrName 属性名
	 * @return 属性値
	 * @throws Exception XML解析時に発生する例外
	 */
	public String getAttrVal(String query, String attrName) throws Exception
	{
		NodeList list = getItemAsNodeList(query);
		if (list.getLength() == 0)
		{
			return null;
		}
		return ((Element)list.item(0)).getAttribute(attrName);
	}

	/**
	 * DOM(ツリーベース)で取得した２つのXML文書のサイズをチェックします。
	 * 
	 * ・メモリーに展開しているXML文書と引数のXML文書のサイズを比較。
	 * 
	 * <br>
	 * @param doc DOMで取得したXML文書
	 * @return true 
	 * @throws Exception
	 */
	private boolean checkXmlSize(Document doc) throws Exception
	{
		// 引数のXML文書のサイズを取得。
		int paramDomSize = getDomSize(doc);

		Document memoryDoc = getDocument();
		if (memoryDoc == null)
		{
			// メモリー上に保持していない場合（初回時）
			outputBusLog("INFO：　初回：XMLのサイズ[B]メモリー上＝【0】読込ファイル＝【" + paramDomSize + "】");
			return false;
		}

		// メモリー上のXML文書のサイズを取得。
		int memoryDomSize = getDomSize(memoryDoc);

		if (memoryDomSize > 0 && paramDomSize > 0 && memoryDomSize == paramDomSize)
		{
			// メモリーに保持しているXML文書と引数のXML文書のサイズが0より大きく、且つサイズが同じ場合
			outputBusLog("INFO：　一致：XMLのサイズ[B]メモリー上＝【" + memoryDomSize + "】読込ファイル＝【" + paramDomSize + "】");

			return true;
		}
		outputBusLog("INFO：　不一致：XMLのサイズ[B]メモリー上＝【" + memoryDomSize + "】読込ファイル＝【" + paramDomSize + "】");
		return false;
	}

	/**
	 * DOM(ツリーベース)で取得したXML文書のサイズを取得します。
	 * 
	 * <br>
	 * @param doc XML文書
	 * @return XML文書のサイズ
	 * @throws Exception
	 */
	private int getDomSize(Document doc) throws Exception
	{
		StringWriter sw = new StringWriter();
		TransformerFactory transformerFactory = TransformerFactory.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		transformer.transform(new DOMSource(doc), new StreamResult(sw));
		sw.flush();
		return sw.getBuffer().length();
	}

	/**
	 * XPathオブジェクトを取得します。
	 * <br>
	 * @return xpath
	 */
	protected XPath getXpath()
	{
		return xpath;
	}

	/**
	 * XPathオブジェクトを設定します。
	 * <br>
	 * @param obj
	 */
	protected void setXpath(XPath obj)
	{
		this.xpath = obj;
	}

	/**
	 * Documentオブジェクトを取得します。
	 * <br>
	 * @return document
	 */
	protected Document getDocument()
	{
		return document;
	}

	/**
	 * Documentオブジェクトを設定します。
	 * <br>
	 * @param obj
	 */
	protected void setDocument(Document obj)
	{
		this.document = obj;
	}

	/**
	 * XML文書全体のサイズチェック結果を取得します。
	 * <br>
	 * @return checkXmlSize
	 */
	protected boolean isCheckXmlSize()
	{
		return checkXmlSize;
	}

	/**
	 * XML文書全体のサイズチェック結果を設定します。
	 * <br>
	 * @param args
	 */
	protected void setCheckXmlSize(boolean arg0)
	{
		this.checkXmlSize = arg0;
	}

	/**
	 * ビジネスログへの出力処理
	 *
	 * @param msg ログ情報
	 */
	protected void outputBusLog(String msg) throws Exception
	{
		JSYwebLog.println(JSYLogBase.EXECUTION, getClass(), msg, "CS0003I", null, null);
	}

// ANK-3715-00-00 ADD START
	/**
	 * 指定された属性値一覧を返却する
	 * @param query
	 * @param attrName
	 * @return
	 * @throws Exception
	 */
	public ArrayList<String> getAttrList(String query, String attrName) throws Exception
	{
		NodeList list = getItemAsNodeList(query);
		ArrayList<String> retList = new ArrayList<String>();
		if (list.getLength() == 0)
		{
			return null;
		}
		for (int i = 0; i < list.getLength(); i++)
		{
			retList.add(((Element)list.item(i)).getAttribute(attrName));
		}
		return retList;
	}
// ANK-3715-00-00 ADD END
}
