/*
 履歴:
 NO	  日付    内容
 001  2021.04.26  新規作成
 */
package com.galaxygoby;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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

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

/**
 * <DL>
 * <DT>クラス概要:
 * <DD>xmlファイルの読込みおよび解析、情報返却を実行するクラス
 * </DL>
 */
public class ShmXMLList {

	/** 空行 */
	private static final String BLANK = "";

	/** スラッシュ */
	private static final String SLASH = "/";

	/** アットマーク */
	private static final String AMARK = "@";

	/** スラッシュ、アットマーク */
	private static final String SLASH_AMARK = "/@";

	/** スラッシュ、アットマーク、文字列（text） */
	private static final String SLASH_AMARK_TEXT = "/@text";

	/** 等号（=） */
	private static final String EQUAL = "=";

	/** 文字列（parm） */
	private static final String STR_PARM = "parm";

	/** 文字列（tag） */
	private static final String STR_TAG = "tag";

	/** 文字列（text） */
	private static final String STR_TEXT = "text";

	/** 文字列（#text） */
	private static final String STR_SHARP_TEXT = "#text";

	/** 文字列（pval） */
	private static final String STR_PVAL = "pval";

	/** 改行コード */
	private static final String BREAK_CODE = "\n";

	/** タブコード */
	private static final String TAB_CODE = "\t";

	/** アットマーク（char型） */
	private static final char CHAR_AMARK = '@';
	
	/** 区切り文字 */
	public static final String SEP_XML = "\0";

	List<List<String>> xmlList = new ArrayList<List<String>>();

	// Node root = null;
	Document root = null;

	int NodesNum = 0;

	/**
	 * <DL>
	 * <DT>コンストラクタ。
	 * <DT>指定されたファイルから、再帰的に情報解析を行い、xmlListに情報を格納する
	 * </DL>
	 * 
	 * @param file xmlファイル名称
	 */
	public ShmXMLList(String file) {
		DocumentBuilderFactory factory;
		DocumentBuilder builder;
		// Node child;
		// NodeList XMLdata;
		try {
			factory = DocumentBuilderFactory.newInstance();
			builder = factory.newDocumentBuilder();
			factory.setIgnoringElementContentWhitespace(true);
			factory.setIgnoringComments(true);
			factory.setValidating(false);
			// root = (Node)builder.parse(file);
			root = builder.parse(file);
		} catch (ParserConfigurationException e0) {
			throw new RuntimeException(e0);
		} catch (SAXException e1) {
			throw new RuntimeException(e1);
		} catch (IOException e2) {
			throw new RuntimeException(e2);
		}
		setList();
	}

	/**
	 * <DL>
	 * <DT>コンストラクタが生成した「List<List<String>> xmlList」をそのまま返却する。
	 * </DL>
	 */
	public List<List<String>> getList() {
		return (xmlList);
	}

	/**
	 * <DL>
	 * <DT>tree指定された情報を元にxmlListから必要な情報を返却する。
	 * </DL>
	 * 例） <Sort> <SortInfo ID="SORT_TEST1" TYP="FIXED"> <Keys T="String">0,5</Keys> </SortInfo> </Sort> (1)
	 * 上記のxmlファイルから、キー情報（0,5）を取得する場合、 「/Sort/SortInfo/Keys」をKeyに指定する。 (2) 上記のxmlファイルから、キー情報のTパラメータ（String）を取得する場合、
	 * 「/Sort/SortInfo/Keys/@T」をKeyに指定する。
	 * 
	 * @param Key 検索キー情報。
	 * @return 検索キーにマッチした要素を返却。マッチする要素が複数あれば、全て返却する。
	 */
	public String[] getValue(String Key) {

		List<String> val = new ArrayList<String>();

		// if (Key.substring(0,1).equals("/") == false) {
		if (SLASH.equals(Key.substring(0, 1)) == false) {
			val = (List<String>)null;
			return (null);
		}
		String[] tag = Key.split(SLASH);
		for (int i = 0; i < xmlList.size(); i++) {
			List<String> xmlData = xmlList.get(i);

			int j = 1;
			int k = 0;
			for (; j < tag.length && k < xmlData.size() - 1; j++, k += 2) {
				// if (tag[j].substring(0,1).equals("@") == true) {
				if (AMARK.equals(tag[j].substring(0, 1)) == true) {
					for (; k < xmlData.size(); k += 2) {
						// OKケース
						// if (xmlData.get(k).equals("parm") == true &&
						if (STR_PARM.equals(xmlData.get(k)) == true && (AMARK + xmlData.get(k + 1)).equals(tag[j]) == true) {
							break;
						}
						// NGケース
						// if (xmlData.get(k).equals("tag") == true ||
						// xmlData.get(k).equals("text") == true) {
						if (STR_TAG.equals(xmlData.get(k)) == true || STR_TEXT.equals(xmlData.get(k)) == true) {
							break;
						}
					}
					break;
				}
				else {
					for (; k < xmlData.size(); k += 4) {
						// if (xmlData.get(k).equals("tag") == true) {
						if (STR_TAG.equals(xmlData.get(k)) == true) {
							break;
						}
					}
					// NGケース
					if (k >= xmlData.size()) {
						break;
					}
					if (xmlData.get(k + 1).equals(tag[j]) == false) {
						break;
					}
					// OKケース
					if (j == tag.length - 1) {
						break;
					}
				}
			}
			/*
			 * if ( k < xmlData.size() ) {
			 * System.out.println("j="+j+" tag.length="+tag.length+" k="+k+" xmlData.get(k)="+xmlData.get(k)) ; } else {
			 * System.out.println("j="+j+" tag.length="+tag.length+" k="+k) ; }
			 */
			if (j == tag.length - 1 && k < xmlData.size() - 1 &&
			// xmlData.get(k).equals("tag") == true &&
					STR_TAG.equals(xmlData.get(k)) == true && xmlData.get(k + 1).equals(tag[j]) == true) {
				k += 2;
				for (; k < xmlData.size(); k += 2) {
					// if ( xmlData.get(k).equals("text") == true ) {
					if (STR_TEXT.equals(xmlData.get(k)) == true) {
						val.add(xmlData.get(k + 1));
						break;
					}
					// if ( xmlData.get(k).equals("tag") == true ) {
					if (STR_TAG.equals(xmlData.get(k)) == true) {
						break;
					}
				}
			}
			else if (j == tag.length - 1 && k < xmlData.size() - 1 && STR_PARM.equals(xmlData.get(k)) == true) {
				// xmlData.get(k).equals("parm") == true ) {
				k += 2;
				// if ( xmlData.get(k).equals("pval") == true ) {
				if (STR_PVAL.equals(xmlData.get(k)) == true) {
					val.add(xmlData.get(k + 1));
				}
			}
		}
		// System.out.println("val.size()=" + val.size()) ;
		if (val.size() <= 0) {
			val = (List<String>)null;
			return (null);
		}

		String[] ret = new String[val.size()];
		for (int i = 0; i < val.size(); i++) {
			ret[i] = val.get(i);
		}
		return (ret);
	}

	public String[][] getFrame(String prmDir) {

		List<String> prmList = new ArrayList<String>();
		String[] word = prmDir.split(SLASH);
		for (int i = 1; i < word.length; i++) {
			// if (word[i].indexOf('@') >= 0) {
			if (word[i].indexOf(CHAR_AMARK) >= 0) {
				String[] word1 = word[i].split(AMARK);
				for (int j = 1; j < word1.length; j++) {
					String[] word2 = word1[j].split(EQUAL);
					// prmList.add("parm");
					prmList.add(STR_PARM);
					prmList.add(word2[0]);
					// prmList.add("pval");
					prmList.add(STR_PVAL);
					prmList.add(word2[1]);
				}
			}
			else {
				// prmList.add("tag");
				prmList.add(STR_TAG);
				prmList.add(word[i]);
			}
		}
		List<String> retValue1 = getFrame((String[])prmList.toArray(new String[0]));
		if (retValue1 == null) {
			return (null);
		}
		List<String> retValue2 = new ArrayList<String>();
		for (int i = 0; i < retValue1.size(); i++) {
			// String[] word1 = retValue1.get(i).split("\0");
			String[] word1 = retValue1.get(i).split(SEP_XML);
			String word2 = BLANK;
			for (int j = 0; j < word1.length;) {
				// if (word1[j].equals("tag") == true) {
				if (STR_TAG.equals(word1[j]) == true) {
					word2 += SLASH + word1[j + 1];
					j += 2;
				}
				else
				// if (word1[j].equals("parm") == true) {
				if (STR_PARM.equals(word1[j]) == true) {
					if (word2.equals(BLANK) == true && i > 0) {
						j += 4;
						continue;
					}
					// retValue2.add(word2 + "/@" + word1[j+1]);
					retValue2.add(word2 + SLASH_AMARK + word1[j + 1]);
					retValue2.add(word1[j + 3]);
					j += 4;
				}
				else
				// if (word1[j].equals("text") == true) {
				if (STR_TEXT.equals(word1[j]) == true) {
					// retValue2.add(word2 + "/@text");
					retValue2.add(word2 + SLASH_AMARK_TEXT);
					retValue2.add(word1[j + 1]);
					j += 2;
				}
			}
		}
		String ret[][] = new String[retValue2.size() / 2][2];
		for (int i = 0; i < retValue2.size(); i += 2) {
			ret[i / 2][0] = retValue2.get(i);
			ret[i / 2][1] = retValue2.get(i + 1);
		}
		return (ret);
	}

	/**
	 * <DL>
	 * <DT>構造化指定された情報を元にxmlListから必要な情報を返却する。</DT>
	 * 例） <Sort> <SortInfo ID="SORT_TEST1" TYP="FIXED"> <Keys T="String">0,5</Keys> </SortInfo> <SortInfo ID="SORT_TEST2"
	 * TYP="VARIABLE"> <Keys T="Integer" S="desc">0</Keys> </SortInfo> </Sort> (1) 上記のxmlファイルから、ID="SORT_TEST2"配下を全て取得する場合、 「tag
	 * Sort tag SortInfo parm ID pval SORT_TEST2」をprmに指定する。（タブ区切り） 返却値は、下記のフォーマットで返却を行う 「tag Sort tag SortInfo parm ID pval
	 * SORT_TEST2 parm TYP pval VARIABLE tag Keys parm T pval Integer parm parm S pval desc text 0」
	 * 
	 * @param prm 検索キー情報。
	 * @return 検索キーにマッチしたフレーム要素を返却。マッチする要素が複数あれば、List形式で全て返却する。
	 */
	public List<String> getFrame(String[] prm) {

		List<String> retValue = new ArrayList<String>();

		// for ( int i=0 ; i<prm.length ; i++ )
		// System.out.println ( "prm[" + i + "]=[" + prm[i] + "]" ) ;

		int j = 0;
		int found = 0;
		for (int i = 0; i < xmlList.size(); i++) {
			List<String> xmlData = xmlList.get(i);
			for (j = 0; j < prm.length; j++) {
				// System.out.println ( "prm[" + j + "]=" + prm[j] ) ;
				// System.out.println ( "xmlData.get(" + j + ")=" + xmlData.get(j) ) ;
				// if (prm[j].equals("") == true || prm[j].equals(xmlData.get(j)) == true) {
				if (BLANK.equals(prm[j]) == true || prm[j].equals(xmlData.get(j)) == true) {
					continue;
				}
				else {
					break;
				}
			}
			if (j == prm.length) {
				String retStr = new String();
				String sep = BLANK;
				for (; j < xmlData.size(); j++) {
					retStr += sep + xmlData.get(j);
					// sep = "\0";
					sep = SEP_XML;
				}
				retValue.add(retStr);
				found = 1;
			}
			else {
				if (found == 1) {
					break;
				}
			}
		}

		if (retValue.size() <= 0) {
			retValue = (List<String>)null;
		}

		return (retValue);
	}

	/**
	 * <DL>
	 * <DT>nodeの子ノードを全てList<List<String>>にセットする。（再帰処理をしているので注意）
	 * </DL>
	 */
	private void setList() {
		showNodes(root, null);
		for (int i = 0; i < xmlList.size() - 1;) {
			List<String> wList1 = xmlList.get(i);
			List<String> wList2 = xmlList.get(i + 1);
			int k = 1;
			for (int j = 0; j < wList1.size(); j++) {
				if (j >= wList2.size()) {
					k = 2;
					break;
				}
				if (wList1.get(j).equals(wList2.get(j)) == false) {
					k = 0;
					break;
				}
			}
			if (k == 0) {
				i++;
			}
			else if (k == 1) {
				xmlList.remove(i);
			}
			else if (k == 2) {
				xmlList.remove(i + 1);
			}
		}
		return;
	}

	/**
   */
	// private void showNodes (Node node, List<String> wl) {
	private void showNodes(Document node, List<String> wl) {
		NodeList nodes = node.getChildNodes();
		if (nodes.getLength() == 0) {
			xmlList.add(wl);
		}
		NodesNum = nodes.getLength();
		for (int i = 0; i < nodes.getLength(); i++) {
			List<String> ls = new ArrayList<String>();
			if (wl != null && wl.size() > 0) {
				for (int k = 0; k < wl.size(); k++) {
					ls.add(wl.get(k));
				}
			}
			Node node2 = (Node)nodes.item(i);
			String Value = null;
			if (node2.getNodeValue() != null) {
				// Value = node2.getNodeValue().replaceAll("\n", "");
				// Value = Value.replaceAll("\t", "");
				Value = node2.getNodeValue().replaceAll(BREAK_CODE, BLANK);
				Value = Value.replaceAll(TAB_CODE, BLANK);
			}
			// if (Value != null && Value.equals("") == true) {
			if (Value != null && BLANK.equals(Value) == true) {
			}
			else {
				// if (node2.getNodeName().equals("#text") == false) {
				if (STR_SHARP_TEXT.equals(node2.getNodeName()) == false) {
					// ls.add("tag");
					ls.add(STR_TAG);
					ls.add(node2.getNodeName());
				}
				if (Value != null) {
					// ls.add("text");
					ls.add(STR_TEXT);
					ls.add(Value);
				}
				for (int j = 0; node2.getAttributes() != null && node2.getAttributes().item(j) != null; j++) {
					// ls.add("parm");
					ls.add(STR_PARM);
					ls.add(node2.getAttributes().item(j).getNodeName());
					// ls.add("pval");
					ls.add(STR_PVAL);
					ls.add(node2.getAttributes().item(j).getNodeValue());
				}
			}
			showNodes(node2, ls);
		}
	}

	/**
	   */
	private void showNodes(Node node, List<String> wl) {
		NodeList nodes = node.getChildNodes();
		if (nodes.getLength() == 0) {
			xmlList.add(wl);
		}
		NodesNum = nodes.getLength();
		for (int i = 0; i < nodes.getLength(); i++) {
			List<String> ls = new ArrayList<String>();
			if (wl != null && wl.size() > 0) {
				for (int k = 0; k < wl.size(); k++) {
					ls.add(wl.get(k));
				}
			}
			Node node2 = (Node)nodes.item(i);
			String Value = null;
			if (node2.getNodeValue() != null) {
				// Value = node2.getNodeValue().replaceAll("\n", "");
				// Value = Value.replaceAll("\t", "");
				Value = node2.getNodeValue().replaceAll(BREAK_CODE, BLANK);
				Value = Value.replaceAll(TAB_CODE, BLANK);
			}
			// if (Value != null && Value.equals("") == true) {
			if (Value != null && BLANK.equals(Value) == true) {
			}
			else {
				// if (node2.getNodeName().equals("#text") == false) {
				if (STR_SHARP_TEXT.equals(node2.getNodeName()) == false) {
					// ls.add("tag");
					ls.add(STR_TAG);
					ls.add(node2.getNodeName());
				}
				if (Value != null) {
					// ls.add("text");
					ls.add(STR_TEXT);
					ls.add(Value);
				}
				for (int j = 0; node2.getAttributes() != null && node2.getAttributes().item(j) != null; j++) {
					// ls.add("parm");
					ls.add(STR_PARM);
					ls.add(node2.getAttributes().item(j).getNodeName());
					// ls.add("pval");
					ls.add(STR_PVAL);
					ls.add(node2.getAttributes().item(j).getNodeValue());
				}
			}
			showNodes(node2, ls);
		}
	}

}
