package com.ftinc.si.assist.test.web;

import java.awt.Color;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.PointerInfo;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.SpinnerNumberModel;

import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.ie.InternetExplorerDriver;
import org.openqa.selenium.ie.InternetExplorerOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.ftinc.si.assist.run.Messages;
import com.ftinc.si.assist.test.Tool;

public class XpathGetter extends JFrame {
	private WebDriver m_driver = null;
	private String m_driverName = null;

	private DefaultListModel<String> m_xpath_model;

	private JTextField m_textCurrentPath;
	private JCheckBox m_chckbxGID;
	private HashMap<String, Object> m_cur_map;
	private String m_repXPath = ""; //代表のxpath //$NON-NLS-1$

	private HashMap<String, Object> m_path_map;
	private String m_target_attr;
	private boolean m_gonext = false;
	private HashMap<String, Object> m_result = null;
	private HashMap<String, String> m_repAttrs = null;

	public XpathGetter() {
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				doClose();
			}
		});
		setResizable(false);

		m_path_map = new HashMap<String, Object>();

		setTitle("Xpath Getter"); //$NON-NLS-1$
		getContentPane().setLayout(null);

		JLabel lblBrowserSelect = new JLabel(Messages.getString("XpathGetter.2")); //$NON-NLS-1$
		lblBrowserSelect.setForeground(Color.RED);
		lblBrowserSelect.setBounds(12, 10, 77, 13);
		getContentPane().add(lblBrowserSelect);

		final JComboBox<String> browsersComboBox = new JComboBox<String>();
		browsersComboBox.addItem("Chrome"); //$NON-NLS-1$
		browsersComboBox.addItem("IE"); //$NON-NLS-1$
		browsersComboBox.addItem("Firefox"); //$NON-NLS-1$
		browsersComboBox.addItem("Edge"); //$NON-NLS-1$

		browsersComboBox.setBounds(92, 7, 171, 19);
		getContentPane().add(browsersComboBox);

		JButton browserButton = new JButton(Messages.getString("XpathGetter.3")); //$NON-NLS-1$
		browserButton.setBounds(294, 6, 91, 21);
		getContentPane().add(browserButton);

		JLabel tagSelectLabel = new JLabel(Messages.getString("XpathGetter.4")); //$NON-NLS-1$
		tagSelectLabel.setBounds(12, 43, 50, 13);
		getContentPane().add(tagSelectLabel);

		final JComboBox<String> tagComboBox = new JComboBox<String>();
		tagComboBox.addItem("div"); //$NON-NLS-1$
		tagComboBox.addItem("input"); //$NON-NLS-1$
		tagComboBox.addItem("li"); //$NON-NLS-1$
		tagComboBox.setSelectedIndex(-1);

		tagComboBox.setEditable(true);
		tagComboBox.setBounds(92, 40, 103, 19);
		getContentPane().add(tagComboBox);

		JLabel selectAttrsLabel = new JLabel(Messages.getString("XpathGetter.5")); //$NON-NLS-1$
		selectAttrsLabel.setForeground(Color.RED);
		selectAttrsLabel.setBounds(218, 43, 77, 13);
		getContentPane().add(selectAttrsLabel);

		final JComboBox<String> attrsComboBox = new JComboBox<String>();
		attrsComboBox.addItem("id"); //$NON-NLS-1$
		attrsComboBox.addItem("name"); //$NON-NLS-1$
		attrsComboBox.addItem("class"); //$NON-NLS-1$
		attrsComboBox.addItem("aria-describedby"); //$NON-NLS-1$
		attrsComboBox.setSelectedIndex(0);//デフォルト値はい
		attrsComboBox.setEditable(true);
		attrsComboBox.setBounds(294, 40, 155, 19);
		getContentPane().add(attrsComboBox);

		JLabel timerLabel = new JLabel(Messages.getString("XpathGetter.6")); //$NON-NLS-1$
		timerLabel.setForeground(Color.RED);
		timerLabel.setBounds(461, 43, 68, 13);
		getContentPane().add(timerLabel);

		final JButton btnScan = new JButton(Messages.getString("XpathGetter.7")); //$NON-NLS-1$

		browserButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (m_driver == null) {
					int n = browsersComboBox.getSelectedIndex();
					if (n >= 0) {
						m_driverName = browsersComboBox.getItemAt(n).toString();
						m_driver = getDriver(m_driverName);

						//ブラウザを変更できないようにする。
						browsersComboBox.setEditable(false);
						browsersComboBox.setEnabled(false);
						btnScan.setEnabled(true);
					}
				}
			}
		});

		SpinnerNumberModel model = new SpinnerNumberModel(5, 0, 20, 1);

		final JSpinner spinner = new JSpinner(model);
		spinner.setBounds(541, 40, 44, 20);
		getContentPane().add(spinner);


		final JButton btnAdd = new JButton(Messages.getString("XpathGetter.8")); //$NON-NLS-1$
		btnScan.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				Object o_tag = tagComboBox.getSelectedItem();
				Object t_attr = attrsComboBox.getSelectedItem();
				Integer t_sec = (Integer)spinner.getValue();
				if (t_attr != null && t_attr.toString().length() > 0) {
					String t_tag = ""; //$NON-NLS-1$
					if (o_tag != null) {
						t_tag = o_tag.toString();
					}
					Integer mil_sec = t_sec * 1000;
					try {
						Thread.sleep(mil_sec);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					String t_path = getScannedXpath(t_tag, t_attr.toString());

					if (t_path != null) {
						btnAdd.setEnabled(true);
					} else {
						Tool.alertMSG(null, Messages.getString("XpathGetter.20")); //$NON-NLS-1$
					}
				} else {
					Tool.alertMSG(null, Messages.getString("XpathGetter.21")); //$NON-NLS-1$
				}
			}
		});
		btnScan.setBounds(612, 39, 80, 21);
		getContentPane().add(btnScan);
		btnScan.setEnabled(false);

		JLabel pathLabel = new JLabel(Messages.getString("XpathGetter.22")); //$NON-NLS-1$
		pathLabel.setBounds(12, 81, 77, 13);
		getContentPane().add(pathLabel);

		m_textCurrentPath = new JTextField();
		m_textCurrentPath.setEditable(false);
		m_textCurrentPath.setBounds(92, 78, 600, 19);
		getContentPane().add(m_textCurrentPath);
		m_textCurrentPath.setColumns(10);

		final JButton btnRemove = new JButton(Messages.getString("XpathGetter.23")); //$NON-NLS-1$
		final JButton btnEdit = new JButton(Messages.getString("XpathGetter.24")); //$NON-NLS-1$

		btnAdd.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//Listが空でない時削除を有効化
				String t_path = m_textCurrentPath.getText();
				if (t_path.length() > 0 && !m_path_map.containsKey(t_path) && !m_repXPath.equals(t_path)) {
					m_xpath_model.addElement(t_path);
					m_path_map.put(t_path, m_cur_map);

					btnRemove.setEnabled(true);
					btnEdit.setEnabled(true);

					//一度決めたタグは変えられない。xpathを複数選べるようにするのは複合部品を考慮しているためなので。
					attrsComboBox.setEnabled(false);
					tagComboBox.setEditable(false);

					if (m_xpath_model.size() > 1) {
						//要素が二つ以上の場合、GroupIDを指定できるようにする。
						m_chckbxGID.setEnabled(true);
					}
					//代表xpathがないなら空に戻す。あれば代表xpathに戻す。
					m_textCurrentPath.setText(m_repXPath);
				}
			}
		});
		btnAdd.setBounds(12, 124, 77, 21);
		getContentPane().add(btnAdd);
		btnAdd.setEnabled(false);

		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(92, 256, 600, 61);
		getContentPane().add(scrollPane_1);

		final JTextPane textDetail = new JTextPane();
		scrollPane_1.setViewportView(textDetail);

		m_xpath_model = new DefaultListModel<String>();
		final JList<String> xpathList = new JList<String>(m_xpath_model);
		xpathList.addFocusListener(new FocusAdapter() {
			@Override
			public void focusGained(FocusEvent e) {
				xpathList.setBackground(new Color(245,245,245));
			}
			@Override
			public void focusLost(FocusEvent e) {
				xpathList.setBackground(Color.white);
			}
		});
		xpathList.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				//getKeyTextはなぜか大文字になる。
//				if (KeyEvent.getKeyModifiersText(e.getModifiers()).toLowerCase().indexOf("ctrl") >= 0 && KeyEvent.getKeyText(e.getKeyCode()).toLowerCase().equals("q")) {
				if (e.isControlDown() && KeyEvent.getKeyText(e.getKeyCode()).toLowerCase().equals("q")) {
					//cntrl+qで現在のマウスの位置のコントロールを登録する。

					Object o_tag = tagComboBox.getSelectedItem();
					Object t_attr = attrsComboBox.getSelectedItem();
					if (t_attr != null && t_attr.toString().length() > 0) {
						String t_tag = ""; //$NON-NLS-1$
						if (o_tag != null) {
							t_tag = o_tag.toString();
						}
						String t_path = getScannedXpath(t_tag, t_attr.toString());
						if (t_path != null) {
							m_xpath_model.addElement(t_path);
							m_path_map.put(t_path, m_cur_map);

							btnRemove.setEnabled(true);
							btnEdit.setEnabled(true);

							//一度決めたタグは変えられない。xpathを複数選べるようにするのは複合部品を考慮しているためなので。
							attrsComboBox.setEnabled(false);
							tagComboBox.setEditable(false);

							if (m_xpath_model.size() > 1) {
								//要素が二つ以上の場合、GroupIDを指定できるようにする。
								m_chckbxGID.setEnabled(true);
							}
						} else {
							Tool.alertMSG(null, Messages.getString("XpathGetter.20")); //$NON-NLS-1$
						}
					} else {
						Tool.alertMSG(null, Messages.getString("XpathGetter.21")); //$NON-NLS-1$
					}
				}
			}
		});
		xpathList.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//クリックしたら、その要素の情報を下部のテキストに表示する。
				int i = xpathList.getSelectedIndex();
				if (i >= 0) {
					String t_str = xpathList.getSelectedValue();
					m_textCurrentPath.setText(t_str);

					String t_result = getChildElementResult(i);
					textDetail.setText(t_result);
				}
			}
		});

		btnRemove.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				int i = xpathList.getSelectedIndex();
				if (i >= 0) {
					String t_str = m_xpath_model.elementAt(i);
					m_xpath_model.remove(i);
					m_path_map.remove(t_str);

					if (m_xpath_model.size() == 0) {
						btnRemove.setEnabled(false);
						btnEdit.setEnabled(false);

						//要素がなくなれば、再設定して走査できるようにする。。
						attrsComboBox.setEnabled(true);
						tagComboBox.setEditable(true);
					}
					if (m_xpath_model.size() < 2) {
						//要素が１以下の場合、GroupIDは指定できない。
						m_chckbxGID.setEnabled(false);
					}
				}
			}
		});
		btnRemove.setBounds(12, 165, 77, 21);
		getContentPane().add(btnRemove);
		btnRemove.setEnabled(false);

		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(92, 124, 600, 126);
		getContentPane().add(scrollPane);
		scrollPane.setViewportView(xpathList);

		JButton btnOK = new JButton(Messages.getString("XpathGetter.25")); //$NON-NLS-1$
		btnOK.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				m_gonext = false;
				if (m_xpath_model.getSize() > 0) {
					//何かがあるばあい、保存処理に進めるようにする
					m_gonext = true;
					m_path_map.put("attributeName", m_target_attr); //$NON-NLS-1$
				}
				doClose();
				setVisible(false);
			}
		});
		btnOK.setBounds(237, 330, 91, 21);
		getContentPane().add(btnOK);

		JButton btnCancel = new JButton(Messages.getString("XpathGetter.27")); //$NON-NLS-1$
		btnCancel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				setVisible(false);
				doClose();
			}
		});
		btnCancel.setBounds(384, 330, 91, 21);
		getContentPane().add(btnCancel);

		btnEdit.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//クリックしたら、その要素の情報を下部のテキストに表示する。
				int i = xpathList.getSelectedIndex();
				if (i >= 0) {
					String t_str = xpathList.getSelectedValue();
					editByDialog(t_str);

					//代表xpathを反映する。
					m_textCurrentPath.setText(m_repXPath);

					String t_result = getChildElementResult(i);
					textDetail.setText(t_result);
				}
			}
		});
		btnEdit.setBounds(12, 210, 77, 21);
		btnEdit.setEnabled(false);
		getContentPane().add(btnEdit);

		m_chckbxGID = new JCheckBox(Messages.getString("XpathGetter.28")); //$NON-NLS-1$
		m_chckbxGID.setBounds(12, 330, 103, 21);
		getContentPane().add(m_chckbxGID);
	}

	private String getScannedXpath(String xtag, String xattr) {
		String t_attr = xattr;
		String t_reg = null;
		String attrs[] = xattr.split("\\s*=");
		if (attrs.length > 1) {
			t_attr = attrs[0];
			if (attrs[1].matches("^~\\s.*$")) {
				//正規表現部分のみを切り出す
				t_reg = "/" + attrs[1].replaceFirst("~\\s*", "") + "/";
			} else {
				//完全一致の正規表現
				t_reg = "/^" + attrs[1] + "$/";
			}
		}
		m_target_attr = t_attr;


		//現在のマウスの位置
		PointerInfo pInfo = MouseInfo.getPointerInfo();
		Point p = pInfo.getLocation();

		HashMap<String, Object> t_map = scanBrowser(p, xtag, t_attr, t_reg, "false", null);//スキャンする
		if (t_map != null) { //$NON-NLS-1$
			String t_path = t_map.get("xpath").toString(); //$NON-NLS-1$
			m_textCurrentPath.setText(t_path);

			String id =""; //$NON-NLS-1$
			Object xval = t_map.get(m_target_attr);
			if (xval != null) {
				id = "[@" +  m_target_attr + "='" + xval.toString() + "']"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			}
			t_path = t_path.replaceFirst("^([^/]+)/(.*)$", "$1" + Matcher.quoteReplacement(id) + "/$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

			List<WebElement> t_elms = m_driver.findElements(By.xpath("//" + t_path));
			if (t_elms.size() > 1) {
				//idだけでは決まらない。
				t_map.put("#count", Integer.valueOf(t_elms.size())); //$NON-NLS-1$
			}
			m_cur_map = t_map;

			//key:value
			//  xpath:最終的に検出されたfull xpath
			//  id:　full xpathのid属性値
			//  部分xpath(末端から逆に遡行)：遡行した要素の属性名、属性値のMap
			//  #count: full xpathで検索される要素数。ユニークにできない場合がある。

		}
		//frameを移動しているかも知れないので元に戻す。
		m_driver.switchTo().defaultContent();
		if (t_map != null && t_map.containsKey("xpath")) {
			return t_map.get("xpath").toString();
		}

		return null;
	}

	//アクション作成支援のために、ブラウザが指定された時、対応するドライバを取得する。
	private WebDriver getDriver(String bname) {
		WebDriver drv = null;

		try {
			//単独
			if (bname.equals("Firefox")) { //$NON-NLS-1$
				drv = new FirefoxDriver();
			} else if (bname.equals("IE")) { //$NON-NLS-1$
				InternetExplorerOptions ieCap = new InternetExplorerOptions();
				ieCap.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);
				drv = new InternetExplorerDriver(ieCap);
			} else if (bname.equals("Chrome")) { //$NON-NLS-1$
				drv = new ChromeDriver();
			} else if (bname.equals("Edge")) { //$NON-NLS-1$
				drv = new EdgeDriver();
			}
		} catch (Throwable e) {
			Tool.alertMSG(null, e.getMessage());
			if (drv != null) {
				drv.quit();
			}
			return null;
		}

		return drv;
	}

	private void doClose() {
		if (m_driver != null) {
			m_driver.quit();
		}

		if (doNext()) {
			//保存する。_controlpaths.res
			File t_resfile = Tool._libfile("$LIBDIR\\_controlpaths.res"); //$NON-NLS-1$

			String t_result = getResult();
			if (t_result != null && t_result.length() > 0) {
				try {
					if (!t_resfile.exists()) {
						t_resfile.createNewFile();
					}
				} catch (IOException e1) {
					Tool.alertMSG(null, e1.getMessage());
				}
				FileWriter t_w;
				try {
					t_w = new FileWriter(t_resfile);
					t_w.write(t_result);
					t_w.close();
				} catch (IOException e1) {
					Tool.alertMSG(null, e1.getMessage());
				}
			}
		}
		Tool.destroy();
		System.exit(0);
	}

	private String m_script = "function _Attrs(x){" + //$NON-NLS-1$
			" var _res = null;" + //$NON-NLS-1$
			" var attrs = x.attributes;" + //$NON-NLS-1$
			" for(var i = attrs.length-1; i>=0; i--){" + //$NON-NLS-1$
			"  if (_res === null) {_res = {};}" + //$NON-NLS-1$
			"  var tz=x.getAttribute(attrs[i].nodeName);" + //$NON-NLS-1$
			"  if (tz !== null && tz !== undefined && attrs[i].nodeName!='title') {" + //$NON-NLS-1$  //titleは不要
			"   _res[attrs[i].nodeName] = x.getAttribute(attrs[i].nodeName);}}" + //$NON-NLS-1$
			"  if (_res === null) {return null}" + //$NON-NLS-1$
			" return JSON.stringify(_res);}" + //$NON-NLS-1$
			//ページの左上のスクリーン座標を計算する。
			"var bWidth = (window.outerWidth - window.innerWidth) / 2;" + //$NON-NLS-1$
			"var iScreenX = window.screenX + bWidth;" + //$NON-NLS-1$
			"var iScreenY = (window.outerHeight - window.innerHeight - bWidth) + window.screenY;" + //$NON-NLS-1$
			//スクリーン座標（引数0,1）からクライアント座標への変換
			"var _x = arguments[0] - iScreenX;" + //$NON-NLS-1$
			"var _y = arguments[1] - iScreenY;" + //$NON-NLS-1$
			//要素を検出する。
			"var _elm = document.elementFromPoint(_x,_y);" + //$NON-NLS-1$
			"if (_elm === null || _elm === undefined){return 'Error:elm = null(' + _x + ',' + _y + ')';}" + //$NON-NLS-1$
			"var _idTag = arguments[2].toLowerCase();" + //$NON-NLS-1$
			"var _idAttr = arguments[3];" + //$NON-NLS-1$
			"document.title='Starting to analyze!';" + //$NON-NLS-1$
			"var _reg = XREG;" + //$NON-NLS-1$
			"var _result = {};" + //$NON-NLS-1$
			"var _xpath = \"\";" + //$NON-NLS-1$
			"document.title='Started to analyze!';" + //$NON-NLS-1$

			"while (_elm !== null && _elm !== undefined) {" + //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('4');}" +
			" var _tag = _elm.tagName.toLowerCase();" +    //ここでnullポインタ例外 //$NON-NLS-1$
			//「/」はxpathのノード名に使えないので代わりに「:」を使う。VBAで元に戻す。
			" var _xattr ='';" + //$NON-NLS-1$
			" if (_elm.getAttribute !== undefined) {_xattr =_elm.getAttribute(_idAttr);}" +  //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('5');}" +
			" if (_xpath !== \"\") {_xpath = \"/\" + _xpath;}" + //$NON-NLS-1$
			" _xpath = _tag + _xpath;" + //$NON-NLS-1$
			" var b_attr = (_xattr !== null && _xattr !== undefined);" + //$NON-NLS-1$ //正規表現があれば、その結果。
			" if (b_attr && _reg !== null) {b_attr=_reg.test(_xattr);}" + //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('6');}" +
			" if ((_idTag==\"*\" ||_tag==_idTag) && b_attr) {" + //$NON-NLS-1$
			    //タグが無指定か一致、かつ、id相当の属性があるノードにたどり着いた場合。
			"  if (arguments[4]=='true') {alert('6a.1 ');}" +
			"  document.title=_tag + '@idTag';" + //$NON-NLS-1$
			"  _result[\"xpath\"]= _xpath;" + //$NON-NLS-1$
			"  _result[\"id\"]= _xattr;" + //$NON-NLS-1$
			"  _result[_xpath] = _Attrs(_elm);" + //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('6a.3 ' + JSON.stringify(_result));}" +
			"  break;" +
			" } else if (_tag==\"body\") {" +  //独立系の子孫部品の場合、bodyまで行ってしまう。 //$NON-NLS-1$
			"  document.title=_tag + '@body';" + //$NON-NLS-1$
			"  _result[\"xpath\"]= _xpath;" + //$NON-NLS-1$
			"  break;" +
			" } else if (_tag != undefined) {" +   //それ以外は、もう一つ上層を調べる。 //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('7');}" +
			"  document.title=_tag + '@else';" + //$NON-NLS-1$
			"  _result[_xpath] = JSON.stringify(_Attrs(_elm));" + //$NON-NLS-1$
			"  _elm = _elm.parentNode;" + //$NON-NLS-1$
			" if (arguments[4]=='true') {alert('8');}" +
			" }}" + //$NON-NLS-1$
			"if (arguments[4]=='true') {alert('0');}" +
			"document.title=JSON.stringify(_result);"; //$NON-NLS-1$


	//第5引数がtrueの時、デバッグのためのalertを表示。
	private HashMap<String, Object> scanBrowser(Point p, String tag, String attr, String reg, String bflag, WebElement xframe) {
		JavascriptExecutor jsexe = (JavascriptExecutor)m_driver;

		Integer x = p.x;
		Integer y = p.y;

		//クライアント座標に変換しHTML要素を検出する。
		try {
			String tagx = tag;
			String xscript = m_script;
			if (tagx == null || tagx.length() == 0) {
				tagx = "*";
			}
			if (reg == null) {
				xscript = xscript.replace("XREG", "null");
			} else {
				xscript = xscript.replace("XREG", reg);
			}
			jsexe.executeScript(xscript, x, y, tagx, attr, bflag);
			String t_obj = null;
			if (xframe == null) {
				t_obj = m_driver.getTitle();
			} else {
				t_obj = m_driver.findElement(By.xpath("//title[1]")).getAttribute("innerHTML");
			}

			if (t_obj == null) {
				return null;
			} else	if (!t_obj.startsWith("{")) { //$NON-NLS-1$
				return null;
			}

			@SuppressWarnings("unchecked")
			HashMap<String, Object> t_map = (HashMap<String, Object>)Tool.getObjectfromJSON(HashMap.class, t_obj);
			if (t_map != null) { //$NON-NLS-1$
				String t_path = t_map.get("xpath").toString(); //$NON-NLS-1$
				if (t_path.endsWith("frame")) {
					String frame_path = t_path.replaceFirst("^.*?/?(i*frame)$", "$1");
					Object attr_jobj = t_map.get(frame_path);
					if (attr_jobj != null) {
						@SuppressWarnings("unchecked")
						HashMap<String,Object> t_attrmap = (HashMap<String, Object>)Tool.getObjectfromJSON(HashMap.class, attr_jobj.toString());;

						if (t_attrmap != null) {
							if(!t_path.startsWith("//")) {
								t_path = "//" + t_path;
							}
							String frame_path2 = t_path;
							if (t_attrmap.containsKey("id")) {
								frame_path2 = frame_path2 + "[@id='" + t_attrmap.get("id") + "'][1]";
							} else if (t_attrmap.containsKey("name")) {
								frame_path2 = frame_path2 + "[@name='" + t_attrmap.get("name") + "'][1]";
							}
							WebElement e_frame = m_driver.findElement(By.xpath(frame_path2));

							WebDriverWait xwait = new WebDriverWait(m_driver, 10);
							xwait.until(ExpectedConditions.frameToBeAvailableAndSwitchToIt(e_frame));

							//もう一度、scanする。
							return scanBrowser(p, tag, attr, reg, "false", e_frame);//trueにするとalertでデバッグできる。
						}
					}
					Tool.alertMSG(null, "The target element may be hidden in a frame/ifarme.");
				}
			}
			return t_map;
		} catch (Exception e) {
			Tool.alertMSG(null, e.getClass().getName() + ":" + e.getMessage()); //$NON-NLS-1$
		}
		return null;
	}

	public boolean doNext() {
		return m_gonext;
	}


	//結果を出力時に代表Pathが指定されていれば、それを省くように努力する。
	//n == -1 ->getResultと同じ
	//それ以外：全て
	@SuppressWarnings("unchecked")
	public String getChildElementResult(int n) {
		String t_result = null;
		ArrayList<String> t_list = null;

		String t_key = m_xpath_model.elementAt(n);

		HashMap<String, String> attr_dic = null;
		if (m_result != null) {
			attr_dic = ((HashMap<String, String>)m_result.get(t_key));
		}

		String var_msg = Messages.getString("XpathGetter.0"); //$NON-NLS-1$
		boolean b_var = false;//$VARが必要かどうかのフラグ
		if (n == 0 && m_xpath_model.getSize() == 1) {
			//単独コントロール
			if (m_cur_map.containsKey("#count")) { //$NON-NLS-1$
				//現在のxpathで要素を検索した時にヒットする個数
				b_var = true;
			}

			//単純コントロール
			String id =""; //$NON-NLS-1$
			if (m_cur_map.containsKey(m_target_attr)) {
				id = "[@" +  m_target_attr + "='$ID']"; //$NON-NLS-1$ //$NON-NLS-2$
			}

			String[] tags = m_xpath_model.elementAt(0).split("/"); //$NON-NLS-1$
			t_result = ""; //$NON-NLS-1$
			t_list = new ArrayList<String>(Arrays.asList(tags));
			for (int i = 0; i < t_list.size(); i++) {
				if (i > 0) {
					t_result += "/"; //$NON-NLS-1$
				}
				t_result += tags[i] + getChildAttrs(t_list, i, attr_dic);
			}

			t_result = t_result.replaceFirst("^([^/\\]]+)[/\\]](.*)$", "$1" + Matcher.quoteReplacement(id) + "/$2"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			if (b_var) {
				t_result += var_msg;
			}
		} else if (attr_dic != null && m_xpath_model.getSize() > 1) {
			//複合コントロール
			t_list = new ArrayList<String>();


			HashMap<String, Object> t_map = (HashMap<String, Object>) m_path_map.get(t_key);
			b_var = false;
			if (t_map.containsKey("#count")) {//項目idだけでは決まらない項目。 //$NON-NLS-1$
				//現在のxpathで要素を検索した時にヒットする個数
				b_var = true;
			}

			String sub_path = attr_dic.get("subxpath"); //$NON-NLS-1$
			if (sub_path != null) {
				int n_rep = 0;
				if (m_repXPath.length() > 0) {
					n_rep = m_repXPath.split("/").length; //$NON-NLS-1$
				}

				//属性編集済み。
				String[] sub_elms = sub_path.split("/"); //$NON-NLS-1$

				t_list.addAll(new ArrayList<String>(Arrays.asList(sub_elms)));

				t_result = ""; //$NON-NLS-1$
				for (int i = 0; i < t_list.size(); i++) {
					if (i > 0) {
						t_result += "/"; //$NON-NLS-1$
					}
					t_result += t_list.get(i) + getChildAttrs(t_list, n_rep + i, attr_dic);
				}
			}
			if (b_var) {
				t_result += var_msg;
			}
		} else {
			//独立系？複合コントロールである。代表Pathを除去するように努力する。
			t_result = t_key.replaceFirst("^" + Pattern.quote(m_repXPath + "/"), ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}
		return t_result;
	}

	private String getChildAttrs(ArrayList<String> elms, int n, HashMap<String, String> attr_map) {
		if (attr_map != null) {
			String t_key = ""; //$NON-NLS-1$

			if (m_repXPath.length() > 0) {
				String[] r_elms = m_repXPath.split("/"); //$NON-NLS-1$
				if (n < r_elms.length) {
					//m_repXPathが指定されているならば、キーは頭から。
					t_key = "//"; //$NON-NLS-1$
					for (int i = 0; i < r_elms.length; i++) {
						if (i > n) {
							t_key += "/"; //$NON-NLS-1$
						}
						t_key += r_elms[i];
					}
				}
			}
			if (t_key.length() == 0) {
				for (int i = n; i < elms.size(); i++) {
					if (i > n) {
						t_key += "/"; //$NON-NLS-1$
					}
					t_key += elms.get(i);
				}
			}


			String t_attr = ""; //$NON-NLS-1$
			if (t_key.startsWith("//") && m_repAttrs != null) { //$NON-NLS-1$
				//repの属性である。
				t_attr = m_repAttrs.get(t_key);
			} else {
				//子の属性である。
				t_attr = attr_map.get(t_key);
			}
			if (t_attr != null) {
				//id相当の属性は別途追加するので、編集は無効とする。
				t_attr = t_attr.replaceFirst("^[^\n]*?\\[@" + m_target_attr + "[^\n]*\n", ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

				t_attr = t_attr.replaceAll("^//[^\n]*?\n", "");//コメントの解除 //$NON-NLS-1$ //$NON-NLS-2$
				t_attr = t_attr.replaceAll("\n//[^\n]*?\n", "\n");//コメントの解除 //$NON-NLS-1$ //$NON-NLS-2$
				t_attr = t_attr.replaceAll("\n//[^\n]*?$", "");//コメントの解除 //$NON-NLS-1$ //$NON-NLS-2$
				t_attr = t_attr.replaceAll("^//[^\n]*?$", "");//コメントの解除 //$NON-NLS-1$ //$NON-NLS-2$
				t_attr = t_attr.replace("\n", "");//改行の除去 //$NON-NLS-1$ //$NON-NLS-2$

				return t_attr;
			}
		}
		return ""; //$NON-NLS-1$
	}


	//XPathTemplate一式をStringで返す。改行でつなげる。
	//代表Xpathにtableがあり、第一要素にtr//tdがある場合、第一要素をindexとする。
	public String getResult() {
		String var_msg = Messages.getString("XpathGetter.113"); //$NON-NLS-1$
		boolean b_var = false;
		String t_result = null;
		if (m_xpath_model.getSize() == 1) {
			if (m_cur_map.containsKey("#count")) { //$NON-NLS-1$
				//現在のxpathで要素を検索した時にヒットする個数
				b_var = true;
			}

			//単純コントロール
			t_result = getChildElementResult(0);
			if (b_var) {
				t_result += var_msg;
			}
		} else if (m_result != null && m_result.size() > 0) {
			//複合コントロール
			t_result = ""; //$NON-NLS-1$

			boolean b_table = false;
			String rep_result = ""; //$NON-NLS-1$
			if (m_repXPath.length() > 0) {
				//代表のxpathを作る。  //...//$ELM
				if (m_repXPath.toLowerCase().matches("^.*table[/\\[].*$")) { //$NON-NLS-1$
					//代表値がtableを含む。
					b_table = true;
				}

				//代表XPathの属性辞書。
				HashMap<String, String> attr_dic = m_repAttrs;

				String[] r_elms = m_repXPath.split("/"); //$NON-NLS-1$
				for (int i = 0; i < r_elms.length; i++) {
					if (i > 0) {
						rep_result += "/"; //$NON-NLS-1$
					}
					rep_result += r_elms[i] + getChildAttrs(null, i, attr_dic);

				}
				String id =""; //$NON-NLS-1$
				if (m_cur_map.containsKey(m_target_attr)) {
					//複合部品でGroupIDを持つ場合と持たない場合(DatePickerなど)がある。
					if (m_chckbxGID.isSelected()) {
						id = "[@" +  m_target_attr + "='$GID']"; //$NON-NLS-1$ //$NON-NLS-2$
					} else {
						id = "[@" +  m_target_attr + "='$ID']"; //$NON-NLS-1$ //$NON-NLS-2$
					}
				}
				//1行目:ids条件を入れる。
				rep_result = "//" + rep_result.replaceFirst("^([^/\\]]+)[/\\]](.*)$", "$1" + Matcher.quoteReplacement(id) + "/$2" + Matcher.quoteReplacement("//$ELM") + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$
			}

			for (int i = 0; i < m_xpath_model.size(); i++) {
				//各子要素の編集済みテンプレートの文字列を構成する。
				String t_elmstr = getChildElementResult(i);//返却値は、代表Path\n子Path
				if (t_elmstr != null) {
					if (t_result.length() > 0) {
						//要素の間の区切りに改行を入れる。
						t_result += "\n"; //$NON-NLS-1$
					}
					if (b_table) {
						//table部品の第一要素でありindexのはず。
						if(i == 0 && t_elmstr.toLowerCase().matches("^.*?tr[/\\[].*?td[/\\[].*$")) { //$NON-NLS-1$
							//注意！！　defaultで第一列を識別用としているが、そうでない場合、変更すること。
							t_elmstr = t_elmstr.replaceFirst("^(?i).*?td", Matcher.quoteReplacement("td[1][.contains('$VAR')]")); //$NON-NLS-1$ //$NON-NLS-2$
						} else {
							t_elmstr = t_elmstr.replaceFirst("(?i)tr", Matcher.quoteReplacement("tr[./$KEY]")); //$NON-NLS-1$ //$NON-NLS-2$
						}
					} else {
						//返却値から親Path情報を削除する。
						t_result += t_elmstr;
					}

				}
			}
			t_result = rep_result + t_result;
		}
		return t_result;
	}

	private void editByDialog(String curPath) {
		//XpathEditor4Elementを開き、子孫要素を指定・編集する。最後の引数は代表Xpathの属性マップ
		XpathEditor4Element t_dial = null;
		if (m_xpath_model.getSize() == 1) {
			//要素が一つだけの場合、nullを指定する。これにより、Editorの中では代表Pathを変更できなくする。
			t_dial = new XpathEditor4Element(this, null, curPath, m_path_map, m_repAttrs);
		} else {
			t_dial = new XpathEditor4Element(this, m_repXPath, curPath, m_path_map, m_repAttrs);
		}
		t_dial.setVisible(true);

		//結果を反映する。
		HashMap<String, String> t_result = t_dial.getResult();
		if (t_result != null && t_result.size() > 0) {
			if (m_result == null) {
				m_result = new HashMap<String, Object>();
			}
			//t_result: representative:親のpath
			//          subxpath：現在編集中の子のsubpath
			//          末端から遡上したpath:そのノードで

			m_repXPath = t_result.get("representative"); //$NON-NLS-1$

			t_result.remove("representative"); //$NON-NLS-1$

			m_repAttrs = new HashMap<String, String>();
			for (Entry<String, String> entry: t_result.entrySet()) {
				if (entry.getKey().startsWith("//")) { //$NON-NLS-1$
					m_repAttrs.put(entry.getKey(), entry.getValue());
				}
			}

			//m_resultのキーは元のPath
			m_result.put(curPath, t_result);

			//同じダイアログで再編集用
			m_path_map.put(curPath + "#attrMap", t_result); //$NON-NLS-1$
		}
	}
}
