/*********************************************************************
* All Rights reserved,Copyright (c) K-Opticom
**********************************************************************
*＜プログラム内容＞
*   システム名      ：eo顧客基幹システム
*   モジュール名    ：JCKLcsDataConverter
*   ソースファイル名：JCKLcsDataConverter.java
*   作成者          ：富士通
*   日付            ：2011年07月01日
*＜機能概要＞
*   LCS連携(PMP,AxM,CCMG)にかかるデータ変換機能を提供する。
*＜修正履歴＞
*   バージョン  修正日       修正者      修正内容
*   v1.00.00    2011/07/01   FJ）眞方    新規作成
*
**********************************************************************/

package eo.common.util;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.fujitsu.futurity.model.base.jcc.util.JCCCalendarException;
import com.fujitsu.futurity.model.base.jcc.util.JCCJapaneseCalendar;
import com.fujitsu.futurity.model.base.jcc.util.JCCWestCalendar;


/**
 * <p>LCS連携(PMP,AxM,CCMG)にかかるデータ変換機能を提供する。</p>
 * <BR>
 * @ author 富士通
 */
public class JCKLcsDataConverter extends DefaultHandler {

	@SuppressWarnings("unchecked")
	private Map nowMapRef = null;

	@SuppressWarnings("unchecked")
	private Map dataMap = new HashMap();

	private List<String> elements = new ArrayList<String>();

	@SuppressWarnings("unchecked")
	public Map getDataMap()
	{
		return dataMap;
	}

	/**
	 * XMLを解析しMapに変換する
	 * @param xmlString
	 * @param encoding
	 * @return マップオブジェクト
	 */
	@SuppressWarnings("unchecked")
	public static Map<String, Object> parseXml(String xmlString, String encoding)
	{
		// デフォルトエンコーディング
		if ((encoding == null)||("".equals(encoding)))
		{
			encoding = "UTF-8";
		}

		Map map = null;

		if ((xmlString == null) || (xmlString.isEmpty())) 
		{
			return new HashMap();
		}

		try
		{
			// XMLを解析しMAPに変換する
			InputStream inputStream = new ByteArrayInputStream(xmlString.getBytes(encoding));
			map = parseXml(inputStream, encoding);
		}
		catch (UnsupportedEncodingException e)
		{
			e.printStackTrace();
		}

		return map;
	}

	/**
	 * リクエストから取得したXMLを解析しMapに変換する
	 * @param inputStream
	 * @param encoding
	 * @return マップ
	 */
	@SuppressWarnings("unchecked")
	public static Map parseXml(InputStream inputStream, String encoding)
	{
		// 受信したXMLデータを解析
		JCKLcsDataConverter myXmlParser = new JCKLcsDataConverter();
		try
		{
			SAXParserFactory spfactory = SAXParserFactory.newInstance();
			SAXParser parser = spfactory.newSAXParser();
			InputSource is = new InputSource(new InputStreamReader(inputStream, encoding));
			is.setEncoding(encoding);
			parser.parse(is, myXmlParser);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		catch (ParserConfigurationException e)
		{
			e.printStackTrace();
		}
		catch (SAXException e)
		{
			e.printStackTrace();
		}
		return myXmlParser.getDataMap();
	}

	/**
	 * リクエストから取得したXMLを解析しMapに変換する
	 * @param servletRequest
	 * @return マップ
	 */
	@SuppressWarnings("unchecked")
	public static Map parseXml(InputStream inputStream)
	{
		// 受信したXMLデータを解析
		JCKLcsDataConverter myXmlParser = new JCKLcsDataConverter();
		try
		{
			SAXParserFactory spfactory = SAXParserFactory.newInstance();
			SAXParser parser = spfactory.newSAXParser();
			parser.parse(inputStream, myXmlParser);
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		catch (ParserConfigurationException e)
		{
			e.printStackTrace();
		}
		catch (SAXException e)
		{
			e.printStackTrace();
		}
		return myXmlParser.getDataMap();
	}

	/**
	 * エレメントの開始時
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void startElement(String uri, String localName, String tagName,
			Attributes attr) throws SAXException {

		Object nowObj = dataMap;
		for (int i = 0; i < elements.size(); i++)
		{
			String elementName = elements.get(i);
			Map nowMap = null;
			// オブジェクトがMapの場合
			if (nowObj instanceof Map)
			{
				nowMap = (Map)nowObj;
			}
			// オブジェクトがListの場合
			else if (nowObj instanceof List)
			{
				List nowList = (List)nowObj;
				nowMap = (Map)nowList.get(nowList.size() - 1);
			}
			nowObj = nowMap.get(elementName);
		}
		
		Map nowMap = null;
		// オブジェクトがMapの場合
		if (nowObj instanceof Map)
		{
			nowMap = (Map)nowObj;
		}
		// オブジェクトがListの場合
		else if (nowObj instanceof List)
		{
			List nowList = (List)nowObj;
			nowMap = (Map)nowList.get(nowList.size() -1 );
		}

		// タグがマップに存在する場合
		if (!nowMap.containsKey(tagName))
		{
			nowMap.put(tagName, new HashMap());
		}
		else
		{
			Object nowBase = nowMap.get(tagName);
			List nowList = null;
			if (nowBase instanceof List)
			{
				nowList = (List)nowBase;
			}
			else
			{
				nowList = new ArrayList();
				nowList.add((Map)nowBase);
			}
			nowList.add(new HashMap());
			nowMap.put(tagName, nowList);
		}
		nowMapRef = nowMap;
		elements.add(tagName);
	}

	/**
	 * エレメントのボディ部を取得
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void characters(char[] chars, int offset, int length) throws SAXException
	{
		// 改行、タブ、半角スペースを除去
		String nodeData = new String(chars, offset, length);
		String checkData = nodeData;
		checkData = checkData.replaceAll("\r\n", "");
		checkData = checkData.replaceAll("\n", "");
		checkData = checkData.replaceAll("\t", "");
		checkData = checkData.replaceAll(" ", "");
		if (checkData.length() > 0)
		{
			String nowName = elements.get(elements.size() - 1);
			nowMapRef.put(nowName, nodeData);
		}
	}

	/**
	 * エレメントの終了時
	 */
	@Override
	public void endElement(String arg0, String arg1, String arg2) throws SAXException
	{
		elements.remove(elements.size() - 1);
	}

	/**
	 * ドキュメントの終了時
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void endDocument() throws SAXException
	{
		dataMap = (Map)replaceEmptyMap(dataMap);
	}

	/**
	 * 空のマップをnullに置き換える
	 * @param obj オブジェクト
	 * @return オブジェクト
	 */
	@SuppressWarnings("unchecked")
	private Object replaceEmptyMap(Object obj)
	{
		// オブジェクトがMapの場合
		if (obj instanceof Map)
		{
			Map nowMap = (Map)obj;
			if (nowMap.isEmpty())
			{
				return null;
			}
			else
			{
				Iterator iterator = nowMap.keySet().iterator();
				while (iterator.hasNext())
				{
					String mapKey = (String)iterator.next();
					Object recObj = replaceEmptyMap(nowMap.get(mapKey));
					nowMap.put(mapKey, recObj);
				}
			}
		}
		// オブジェクトがListの場合
		else if (obj instanceof List)
		{
			List nowList = (List)obj;
			for (int i = 0; i < nowList.size(); i++)
			{
				Object nowObj = nowList.get(i);
				nowObj = replaceEmptyMap(nowObj);
				nowList.set(i, nowObj);
			}
		}

		return obj;
	}

	/**
	 * オブジェクトをXML文字列に変換する
	 * @param map マップオブジェクト
	 * @return XML文字列
	 */
	@SuppressWarnings("unchecked")
	public static String parseMapToXml(Map map)
	{
		return parseMapToXml(map, 0);
	}

	/**
	 * オブジェクトをXML文字列に変換する
	 * @param map マップオブジェクト
	 * @param lv 初期インデントレベル
	 * @return XML文字列
	 */
	@SuppressWarnings("unchecked")
	public static String parseMapToXml(Map map, int lv)
	{
		String xml = parseMapToXml(map, new String[]{}, lv, null);
		while (xml.indexOf("\r\n\r\n") != -1)
		{
			xml = xml.replaceAll("\r\n\r\n","\r\n");
		}
		return xml;
	}

	/**
	 * オブジェクトをXML文字列に変換する
	 * @param obj オブジェクト
	 * @param listName リスト名
	 * @return XML文字列
	 */
	@SuppressWarnings("unchecked")
	public static String parseMapToXml(Object obj, String[] listNames, int lv, Map attr)
	{
		StringBuffer sb = new StringBuffer();
		// オブジェクトがnullの場合
		if (obj == null)
		{
			sb.append("");
		}
		// オブジェクトがStringの場合
		else if (obj instanceof String)
		{
			sb.append((String)obj);
		}
		// オブジェクトがIntegerの場合
		else if (obj instanceof Integer)
		{
			sb.append(obj.toString());
		}
		// オブジェクトがMapの場合
		else if (obj instanceof Map)
		{
			Map map = (Map)obj;
			Iterator iterator = map.keySet().iterator();
			while (iterator.hasNext())
			{
				String key = (String)iterator.next();
				Object val = map.get(key);
				if (key.matches(".*#.*"))
				{
					continue;
				}
				Map attrMap = (Map)map.get(key+"#attr");
				String xmlStr = null;
				String crlf = "\r\n";
				if ((val == null) || (val instanceof String) || (val instanceof Integer))
				{
					crlf = "";
				}
				if (val instanceof List)
				{
					xmlStr = parseMapToXml(val, new String[]{key}, lv+1, attrMap);
					sb.append(xmlStr+crlf);
				}
				else
				{
					xmlStr = parseMapToXml(val, new String[]{}, lv+1, attrMap);
					sb.append(getIndentString(lv)+"<"+key+">"+crlf);
					sb.append(xmlStr+crlf);
					if ("\r\n".equals(crlf))
					{
						sb.append(getIndentString(lv));
					}
					sb.append("</"+key+">\r\n");
				}
			}
		}
		// オブジェクトがListの場合
		else if (obj instanceof List)
		{
			List list = (List)obj;
			String key = listNames[0];
			StringBuffer attrSb = new StringBuffer();
			if (attr != null)
			{
				// タグに埋め込む属性を生成
				Iterator<String> it = attr.keySet().iterator();
				while (it.hasNext())
				{
					String attrName = it.next();
					Object attrVal  = attr.get(attrName);
					if (attrSb.length() > 0)
					{
						attrSb.append(" ");
					}
					attrSb.append(attrName);
					attrSb.append("=");
					attrSb.append("\"" + attrVal + "\"");
				}
			}

			// タグを作成
			String attText = (attrSb.length() > 0) ? " " + attrSb.toString() : "";
			if (list == null || list.size() == 0)
			{
				sb.append(getIndentString(lv - 1)+"<" + key + attText + ">\r\n");
				sb.append(getIndentString(lv - 1)+"</" + key + ">\r\n");
			}
			else
			{
				sb.append(getIndentString(lv - 1)+"<" + key + attText + ">\r\n");
				for (Object val : list)
				{
					String xmlStr = parseMapToXml(val, new String[]{}, lv, null);
					sb.append(xmlStr);
				}
				sb.append(getIndentString(lv - 1)+"</" + key + ">\r\n");
			}
		}
		
		return sb.toString();
	}

	/**
	 * インデント用の文字列を返却します。<br />
	 * @param lv レベル
	 * @return インデント文字列
	 */
	private static String getIndentString(int lv){
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < lv; i++)
		{
			sb.append("\t");
		}
		return sb.toString();
	}

	/**
	 * マップから指定されたキーの値を取得する。
	 * (使用例）<br />
	 *     getObjectFromMap(mapObj, "TESTDATA.dataList1[0].TEST_COL");<br />
	 * @param map マップ
	 * @param allKeys キー名
	 * @return 値
	 */
	@SuppressWarnings("unchecked")
	public static Object getObjFromMap(Map mapObj, String columnName)
	{
		String[] colNames = columnName.split("\\.");
		return getObjFromMap(mapObj, colNames);
	}

	/**
	 * マップから指定されたキーの値を取得する。
	 * (使用例）<br />
	 *     getObjectFromMap(mapObj, "TESTDATA" , "dataList1","0", "TEST_COL");<br />
	 * @param map マップ
	 * @param allKeys キー名
	 * @return 値
	 */
	@SuppressWarnings("unchecked")
	public static Object getObjFromMap(Map mapObj, String...colNames)
	{
		Object dataObj = mapObj;
		for (int i = 0; i < colNames.length; i++)
		{
			Map dataMap = (Map)dataObj;
			String tmpColName = colNames[i];

			// キー名が "項目名[n]" の時はリストのn番目の要素を取得
			if (tmpColName.matches(".+\\[[0-9]+\\]$"))
			{
				String listName = tmpColName.replaceAll("\\[[0-9]+\\]", "");
				String indexStr = tmpColName.replace(listName, "").replaceAll("(\\[|\\])", "");;
				int index = Integer.parseInt(indexStr);
				if (dataMap == null)
				{
					return null;
				}
				Object listObj = dataMap.get(listName);
				// オブジェクトがString[]の場合
				if (listObj instanceof String[])
				{
					String[] list = (String[])listObj;
					if ((list == null) || (index > (list.length - 1)))
					{
						return null;
					}
					dataObj = list[index];
				}
				// オブジェクトがInteger[]の場合
				else if (listObj instanceof Integer[])
				{
					Integer[] list = (Integer[])listObj;
					if ((list == null) || (index > (list.length - 1)))
					{
						return null;
					}
					dataObj = list[index];
				}
				// オブジェクトがLong[]の場合
				else if (listObj instanceof Long[])
				{
					Long[] list = (Long[])listObj;
					if ((list == null) || (index > (list.length - 1)))
					{
						return null;
					}
					dataObj = list[index];
				}
				// オブジェクトがListの場合
				else if (listObj instanceof List)
				{
					List list = (List)listObj;
					if ((list == null) || (index > (list.size() - 1)))
					{
						return null;
					}
					dataObj = list.get(index);
				}
			} else {
				// 指定されたオブジェクトを取得
				if (dataMap == null)
				{
					return null;
				}
				dataObj = dataMap.get(tmpColName);
			}
		}
		return dataObj;
	}

	/**
	 * マップから指定されたキーの値を取得する。
	 * (使用例）<br />
	 *     getObjectFromMap(mapObj, "TESTDATA.dataList1[0].TEST_COL");<br />
	 * @param map マップ
	 * @param allKeys キー名
	 * @return 値
	 */
	@SuppressWarnings("unchecked")
	public static List getListFromMap(Map mapObj, String colNames)
	{
		Object obj = getObjFromMap(mapObj, colNames);
		List list = new ArrayList();
		if (obj instanceof Map)
		{
			list.add((Map)obj);
		}
		else if (obj instanceof List)
		{
			list = (List)obj;
		}
		return list;
	}

	/**
	 * マップから指定されたキーの値を取得する。
	 * (使用例）<br />
	 *     getObjectFromMap(mapObj, "TESTDATA" , "dataList1","0", "TEST_COL");<br />
	 * @param map マップ
	 * @param allKeys キー名
	 * @return 値
	 */
	@SuppressWarnings("unchecked")
	public static List getListFromMap(Map mapObj, String...colNames)
	{
		Object obj = getObjFromMap(mapObj, colNames);
		List list = new ArrayList();
		if (obj instanceof Map)
		{
			list.add((Map)obj);
		}
		else if (obj instanceof List)
		{
			list = (List)obj;
		}
		return list;
	}

	/**
	 * 西暦を和暦に変換し、元号、年、月、日に分割されたものを配列で返却します。
	 * <pre>
	 * ≪返却される文字列配列の例≫
	 *     [ "平成", "23", "12" , "8" ]
	 * </pre>
	 * @param date 年月日(8桁)
	 * @return 和暦の文字列配列
	 * @throws JCCCalendarException
	 */
	public static String[] toJapaneseYmdArray(String date) throws JCCCalendarException
	{
		String delims = "/";
		return new JCCJapaneseCalendar(new JCCWestCalendar(date).toJapaneseCalendar()).toCcString(delims, delims, delims, delims).split(delims);
	}
}
