/////////////////////////////////////////////////////////////////
//Copyright (C) 2005 NTT CORPORATION.
//
//	メニューカスタマイズ定義クラス
//
//		変更履歴
//			2005.02.19  新規作成  梅原 浩司
//
//	機能
//
//	このクラスはメニューカスタマイズ定義を保存するクラスです。
//
////////////////////////////////////////////////////////////////

package jp.co.ntt.lms.xmf;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.io.FileOutputStream;
import java.io.File;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult; 

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

import jp.co.ntt.lms.xmf.beans.CustomMenuBean;


/**
 *  メニューカスタマイズ定義クラス
 * 
 *  このクラスはメニューカスタマイズ定義を保存するクラスです。
 * 
 *  @author 梅原 浩司
 *  @version 1.0
 */
public class CustomMenuDef
{
	private String defType = "";
	private String userId = "";
	private String groupId = "";
	
	private Node documentRoot = null;
	private Document document = null;
	
	// メニューカスタマイズ定義種別定数
	public static final String DEFTYPE_DEFAULT = "default";	// デフォルト定義	
	public static final String DEFTYPE_USER = "user";			// ユーザー定義
	public static final String DEFTYPE_GROUP = "group";		// グループ定義
	
	/**
	 * コンストラクタ
	 * 
	 * @param defType	メニューカスタマイズ定義種別
	 * @param userId	定義の対象となるユーザID
	 * @param groupId	定義の対象となるグループID
	 */
	public CustomMenuDef(String defType, String userId, String groupId)
	{
		this.defType = defType;
		this.userId = userId;
		this.groupId = groupId;
	}
	
	/**
	 * メニューカスタマイズ定義をファイルから読み込む。
	 * 
	 * @param parser 	パーサーオブジェクト
	 * @param is		入力ストリームオブジェクト
	 * @throws SAXException
	 * @throws IOException
	 */
	public void loadMenuDef(DocumentBuilder parser, InputStream is)
		throws SAXException, IOException
	{
		document = parser.parse(new InputSource(is));
		documentRoot = document.getFirstChild();
	}
	
	/**
	 * カスタムメニュー定義をファイルに出力する。
	 * 
	 * @param strSaveFileName 出力ファイル名
	 * @throws IOException 
	 */
	public void saveMenuDef(String strSaveFileName)
		throws IOException
	{
		//ファイルへの出力
		try {
			TransformerFactory transFactory = TransformerFactory.newInstance(); 
			Transformer transformer = transFactory.newTransformer(); 

			DOMSource source = new DOMSource(document);
			File newXML = new File(strSaveFileName); 
			FileOutputStream os = new FileOutputStream(newXML);
			StreamResult result = new StreamResult(os);
			transformer.transform(source, result);
			os.close();
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
	
	/**
	 * 定義の対象となるユーザIDを取得する。
	 * 
	 * @return String ユーザーID
	 */
	public String getUserId()
	{
		return userId;
	}
	
	/**
	 * 定義の対象となるグループIDを取得する。
	 * 
	 * @return String グループID
	 */
	public String getGroupId()
	{
		return groupId;
	}
	
	/**
	 * カスタムメニュー定義種別がユーザ定義かどうか。
	 * 
	 * @return boolean true:ユーザ定義である
	 */
	private boolean isUserDef()
	{
		return defType.equals(DEFTYPE_USER);
	}
	
	/**
	 * カスタムメニュー定義種別がグループ定義かどうか。
	 * @return boolean true:グループ定義である
	 */
	private boolean isGroupDef()
	{
		return defType.equals(DEFTYPE_GROUP);
	}
	
	/**
	 * カスタムメニュー定義種別がデフォルト定義かどうか。
	 * 
	 * @return boolean true:デフォルト定義である。
	 */
	private boolean isDefaultDef()
	{
		return defType.equals(DEFTYPE_DEFAULT);
	}
	
	/**
	 * titleとselect属性で指定したメニュー定義がvisibleかどうか。
	 * 
	 * @param menuTitle 検索条件となるtitle属性値
	 * @param selectId 検索条件となるselect属性値
	 * @return boolean true:visibleである
	 */
	public boolean isMenuVisible(String menuTitle, String selectId)
	{
		List list = NodeUtil.getNamedChildElementList(documentRoot, "menu");
		for (int i=0; i < list.size(); i++)
		{
			Node menuNode = (Node)list.get(i);
			if (menuTitle.equals(NodeUtil.getAttribute(menuNode, "title")))
			{
				String selectAttribute = NodeUtil.getAttribute(menuNode, "select");
				if (selectAttribute != null)
				{
					if (selectAttribute.equals(selectId))
					{
						String visibleAttribute = NodeUtil.getAttribute(menuNode, "visible");
						if ("false".equalsIgnoreCase(visibleAttribute))
						{
							return false;
						}
					}
				}
				else
				{
					String visibleAttribute = NodeUtil.getAttribute(menuNode, "visible");
					if ("false".equalsIgnoreCase(visibleAttribute))
					{
						return false;
					}
				}
			}
		}
		return true;
	}
	
	/**
	 * titleとselect属性で指定したメニュー定義のdisplaytitle属性値を取得する。
	 * 
	 * @param menuTitle 検索条件となるtitle属性値
	 * @param selectId 検索条件となるselect属性値
	 * @return String 指定されたdisplaytitile属性値。
	 */
	public String changeMenuTitle(String menuTitle, String selectId)
	{
		List list = NodeUtil.getNamedChildElementList(documentRoot, "menu");
		for (int i=0; i < list.size(); i++)
		{
			Node menuNode = (Node)list.get(i);
			if (menuTitle.equals(NodeUtil.getAttribute(menuNode, "title")))
			{
				String dispTitleAttribute = NodeUtil.getAttribute(menuNode, "displaytitle");
				if (dispTitleAttribute != null)
				{
					String selectAttribute = NodeUtil.getAttribute(menuNode, "select");
					if (selectAttribute != null)
					{
						if (selectAttribute.equals(selectId))
						{
							return dispTitleAttribute;
						}
					}
					else
					{
						return dispTitleAttribute;
					}
				}
			}
		}
		return menuTitle;
	}
	
	/**
	 * カスタムメニュー定義の一覧を取得する。
	 * 
	 * @return List カスタムメニュー定義一覧。
	 */
	public List getCustomMenuList()
	{
		List menuList = new ArrayList();
		
		List list = NodeUtil.getNamedChildElementList(documentRoot, "menu");
		for (int i=0; i < list.size(); i++)
		{
			Node menuNode = (Node)list.get(i);
			
			String title = NodeUtil.getAttribute(menuNode, "title");
			String dispTitle = NodeUtil.getAttribute(menuNode, "displaytitle");
			String visible = NodeUtil.getAttribute(menuNode, "visible");
			String select = NodeUtil.getAttribute(menuNode, "select");

			CustomMenuBean menuBean = new CustomMenuBean();
			menuBean.setTitle(title);
			menuBean.setDisplaytitle(dispTitle);
			menuBean.setVisible(visible);
			menuBean.setSelect(select);
			
			menuList.add(menuBean);
		}
		
		return menuList;
	}
	
	/**
	 * 新規menu要素を追加する。
	 * 
	 * @param strTitle title属性
	 * @param strSelect select属性
	 * @param strDisplayTitle displaytitle属性
	 * @param blnVisible trueならvisible=falseを属性に設定
	 * @return 成功/失敗
	 */
	public boolean addCustomMenu(String strTitle, String strSelect, String strDisplayTitle, boolean blnVisible)
	{
		// 改行のテキストノードを追加。
		Text txtCR = document.createTextNode("\n");
		// 新規ノードを追加。
		Element elmMenu= document.createElement("menu");
		elmMenu.setAttribute("title", strTitle);
		if (blnVisible)
		{
			// visible属性を設定。
			elmMenu.setAttribute("visible", "false");
		}
		else
		{
			// displaytitle属性を設定。
			elmMenu.setAttribute("displaytitle", strDisplayTitle);
		}
		if (!strSelect.equals("ALL") && !strSelect.equals("") )
		{
			// 個別設定の場合、全体設定の前に挿入
			elmMenu.setAttribute("select", strSelect);
			Node nextNode = getInsertPersonalMenuNode();
			if (nextNode == null)
			{
				// 全体設定がなければ最後に追加。
				documentRoot.appendChild(elmMenu);
				documentRoot.appendChild(txtCR);
			}
			else
			{
				// あればその前に追加。
				documentRoot.insertBefore(elmMenu, nextNode);
				documentRoot.insertBefore(txtCR, nextNode);
			}
		}
		else
		{
			// 全体の設定の場合、select属性はなし。
			documentRoot.appendChild(elmMenu);
			documentRoot.appendChild(txtCR);
		}
		
		
		return true;
	}
	
	/**
	 * title,select属性をキーに一致したmenu要素のdisplaytitle属性を更新する。
	 * 
	 * @param strTitle	検索キーとなるtitle文字列
	 * @param strSelect 検索キーとなるselect文字列
	 * @param strDisplayTitle displaytitle属性に設定する文字列
	 * @param blnUnvisible 検索キーとしてvisible="false"を指定するかどうか
	 * @return boolean 成功/失敗
	 */
	public boolean updateCustomMenu(String strTitle, String strSelect, String strDisplayTitle, boolean blnUnvisible)
	{
		Element menuElement = (Element)searchCustomMenu(strTitle, strSelect, blnUnvisible);
		if (menuElement == null) return false;
		
		menuElement.setAttribute("displaytitle", strDisplayTitle);
		
		return true;
	}
	
	/**
	 * title,select属性をキーに一致したmenu要素を削除する。
	 * 
	 * @param strTitle	検索キーとなるtitle文字列
	 * @param strSelect 検索キーとなるselect文字列
	 * @param blnUnvisible 検索キーとしてvisible="false"を指定するかどうか
	 * @return boolean 成功/失敗
	 */
	public boolean deleteCustomMenu(String strTitle, String strSelect, boolean blnUnvisible)
	{
		Node menuNode = searchCustomMenu(strTitle, strSelect, blnUnvisible);
		if (menuNode == null) return false;
		
		// 親ノードから指定ノードを削除する。
		Node parentNode = menuNode.getParentNode();
		Node nextNode = menuNode.getNextSibling();
		parentNode.removeChild(menuNode);
		// 削除するノードの次がテキストノード(改行)なら削除する。
		if (nextNode != null && nextNode.getNodeType() == Node.TEXT_NODE)
		{
			parentNode.removeChild(nextNode);
		}
		
		return true;
	}
	
	/**
	 * title,select属性をキーにmenu要素を検索する。
	 * 
	 * @param strTitle	検索キーとなるtitle文字列
	 * @param strSelect 検索キーとなるselect文字列
	 * @param blnUnvisible 検索キーとしてvisible="false"を指定するかどうか
	 * @return Node 検索結果のmenu要素
	 */
	private Node searchCustomMenu(String strTitle, String strSelect, boolean blnUnvisible)
	{
		if (strSelect.equals("ALL")) strSelect = "";
		List list = NodeUtil.getNamedChildElementList(documentRoot, "menu");
		for (int i=0; i < list.size(); i++)
		{
			Node menuNode = (Node)list.get(i);
			
			String title = NodeUtil.getAttribute(menuNode, "title");
			String select = NodeUtil.getAttribute(menuNode, "select");
			String visible = NodeUtil.getAttribute(menuNode, "visible");
			boolean unvisible = (visible != null && visible.equals("false"));	// visible="false"が指定されているか
			if (title == null) title = "";
			if (select == null) select = "";

			if (strTitle.equals(title) && strSelect.equals(select) && blnUnvisible == unvisible)
			{
				return menuNode;
			}
		}
		return null;
	}
	
	/**
	 * 個別設定menu要素を挿入する場所のmenu要素を取得する。
	 * 
	 * @return Node 個別設定を挿入する場所のmenu要素 
	 */
	private Node getInsertPersonalMenuNode()
	{
		List list = NodeUtil.getNamedChildElementList(documentRoot, "menu");
		for (int i=0; i < list.size(); i++)
		{
			Node menuNode = (Node)list.get(i);
			
			String select = NodeUtil.getAttribute(menuNode, "select");
			
			// 全体設定の最初を検索
			if (select == null)
			{
				// その一個前の要素を取得。
				Node prevNode = menuNode.getPreviousSibling();
				while (prevNode.getNodeType() == Node.TEXT_NODE)
				{
					// 一個前がテキストノードならもう一個前を返す
					prevNode = prevNode.getPreviousSibling();
					if (prevNode == null) break;
				}
				if (prevNode.getNodeType() == Node.COMMENT_NODE)
				{
					// 一個前がコメントならそれを返す。
					return prevNode;
				}
				else
				{
					// そうでないなら全体設定の最初を返す。
					return menuNode;
				}
			}
		}
		return null;	// 全体設定も見つからない場合
	}
}
