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

import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JFrame;

import com.ftinc.si.assist.run.Messages;
import com.ftinc.si.assist.run.plugins.TestCaseCreator;
import com.ftinc.si.assist.test.AssertRecord;
import com.ftinc.si.assist.test.Fson;
import com.ftinc.si.assist.test.JSONRecord;
import com.ftinc.si.assist.test.ObjectRecord;
import com.ftinc.si.assist.test.Record;
import com.ftinc.si.assist.test.TestCaseRecord;
import com.ftinc.si.assist.test.TestCommandRecord;
import com.ftinc.si.assist.test.Tool;


public class WebTestCreator extends TestCaseCreator {
	private static StatusViewer s_viewer = new StatusViewer();

	@Override
	public int command(HashMap<String, Object> map) {
		String cmd = map.get("verb").toString(); //$NON-NLS-1$
		switch (cmd) {
			case "#inspect": return inspect(map); //$NON-NLS-1$
			case "#xpath": return xpathTest(map); //$NON-NLS-1$
			case "#controlpaths": return scanControlPaths(); //$NON-NLS-1$
			default:return super.command(map);
		}
	}

	//WebElementGetterを開く。
	private int xpathTest(HashMap<String, Object> map) {
		WebElementGetter t_getter = new WebElementGetter(null);
		t_getter.setModal(true);
		t_getter.setVisible(true);
		return 1;
	}


	//コントロール辞書の作成支援のためのxpath候補を収集し、情報をVBAに受け渡すためのファイルに書き込む。
	private int scanControlPaths() {
		XpathGetter t_getter = new XpathGetter();
		t_getter.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		t_getter.setLocationRelativeTo(null);
		t_getter.setBounds(12, 12, 720, 400);

		t_getter.setVisible(true);//いったんは通過する。
		return 5;//5は継続。
	}

	//checkpointsとtransfromのリストを使って、辿れるかどうかを確かめる。
	private int inspect(HashMap<String, Object> map) {
		String e_msg = "null"; //$NON-NLS-1$
		if (map.containsKey("from") && map.containsKey("checkpoints")) { //$NON-NLS-1$ //$NON-NLS-2$
			@SuppressWarnings("unchecked")
			ArrayList<String> temp_list = (ArrayList<String>)map.get("checkpoints"); //$NON-NLS-1$
			ArrayList<String> chk_list = new ArrayList<String>();
			for (int i = 0; i < temp_list.size(); i++) {
				final Pattern t_pat1 = Pattern.compile("^(.*?)\\.([^\\(]+)\\(([^\\)]+)\\)$");
				String sdata = temp_list.get(i);

				//checkpointの書式をMacroActionsに合わせる。
				Matcher t_m = t_pat1.matcher(sdata);
				if (t_m.find()) {
					//panel.sname(paramName)
					chk_list.add(t_m.group(1) + "\t" + t_m.group(3) + "\t" + t_m.group(2));
				} else {
					final Pattern t_pat2 = Pattern.compile("^([^\\.\\(]+)\\(([^\\)]+)\\)$");
					t_m = t_pat2.matcher(sdata);
					if (t_m.find()) {
						//panel(paramName)
						chk_list.add(t_m.group(1) + "\t" + t_m.group(2) + "\t");
					} else {
						final Pattern t_pat3 = Pattern.compile("^(.*?)\\.([^\\(]+)$");
						t_m = t_pat3.matcher(sdata);
						if (t_m.find()) {
							//panel(paramName)
							chk_list.add(t_m.group(1) + "\t\t" + t_m.group(2));
						} else {
							chk_list.add(sdata + "\t\t");
						}
					}
				}
			}

			@SuppressWarnings("unchecked")
			ArrayList<String> from_list = (ArrayList<String>)map.get("from"); //$NON-NLS-1$

			if (from_list == null || from_list.size() == 0) {
				Tool.alertMSG(null, Messages.getString("WebTestCreator.12")); //$NON-NLS-1$
				return -1;
			}

			s_viewer.setVisible(true);

			//検証は普通、画面ID#モードのみの指定である。
			String keyact = map.get("start").toString(); //$NON-NLS-1$
			String[] keys = keyact.split("\t", -1); //$NON-NLS-1$
			if (keys.length == 1) {
				keyact = keys[0] + "\t\t"; //$NON-NLS-1$
			}
			JSONRecord t_jdata = JSONRecord.getJSONRec(MacroActions.class.getName(), keys[0] + "_submit"); //$NON-NLS-1$
			if (t_jdata != null ) {
				MacroActions t_macro = new MacroActions();
				Fson.updateFromJson(t_macro, t_jdata.content);
				Fson.updateByJsonPath(t_macro, "$.trans_from", Tool.getJSONfromObject(from_list)); //$NON-NLS-1$


				Fson.updateByJsonPath(t_macro, "$.key4actions", keyact); //$NON-NLS-1$
				t_macro.options = "_submit"; //$NON-NLS-1$

				ArrayList<String> ng_list = new ArrayList<String>();//堂々巡りの防止用
				ng_list.add(map.get("start").toString()); //$NON-NLS-1$

				try {
					ArrayList<ArrayList<String>> xres = t_macro.getHistory(chk_list, ng_list);
					if (xres != null && xres.size() > 0) {
						String t_result = ""; //$NON-NLS-1$
						ArrayList<String> routes = xres.get(0);
						for (int j = 0; j < routes.size(); j++) {
							if (j > 0) {
								t_result += " > "; //$NON-NLS-1$
							}
							String[] t_strs = routes.get(j).split("\t", -1); //$NON-NLS-1$
							t_result += t_strs[0];
						}
						s_viewer.setVisible(false);

						//key4actionsを>でつなげた結果を表示する。
						Tool.alertMSG(null, Messages.getString("WebTestCreator.25") + t_result + " > " + keys[0]); //$NON-NLS-1$ //$NON-NLS-2$
						return 0;
					} else {
						e_msg = t_macro.getErrMsg();
						Tool.alertMSG(null, "history of " + keyact + " not found.\n" + e_msg); //$NON-NLS-1$
					}
				} catch (Exception e) {
					e.printStackTrace();
					e_msg = t_macro.getErrMsg();
					Tool.alertMSG(null, e_msg + " ExceptionMsg=" + e.getMessage()); //$NON-NLS-1$
				}
			} else {
				Tool.alertMSG(null, keyact + Messages.getString("WebTestCreator.28")); //$NON-NLS-1$
			}
			s_viewer.setVisible(false);
		} else {
			Tool.alertMSG(null, Messages.getString("WebTestCreator.29") + e_msg); //$NON-NLS-1$
		}
		return -1;
	}


	//※パラメータ組名：遷移の際のルートにおいて、パラメータ組名の指定はオプションである。
	//　遷移にあたり、該当画面に遷移するパラメータ組名が複数ある場合、指定のものを使う。指定されていない場合、最初にヒットしたものを使う。
	//　パラメータ組名は、分岐にあたりどのような値をセットしたかが、判定のポイントになる場合に指定する。

	//この画面の遷移元リスト：画面ID＋モード/ボタン名/パラメータ組名。最初の要素がデフォルト遷移元。袋小路や、ここから出て行って戻るパターンは含まない。

	//遷移元へ辿るルート：画面ID＋モード/ボタン名/パラメータ組名 のリスト。リスト終了後はデフォルト遷移元を辿る。


	/* 入力文字列：submit型
		<macro_template type="submit/check" panelName=""" & panelId & """>
		<trans_to> {パラメータ組名 \t ボタンID : 遷移先画面ＩＤ＋モード,...}</trans_to>
		<url></url>
		<trans_from>画面ID#モード,...</trans_from>
		<window_title></window_title>
		<frame></frame>
		<checkpoints>
		{パラメータ組名　：　[経由地の(画面ID＋モード \t パラメータ組名 \t ボタン名)のリスト],}
		</checkpoints>
		<outerset>
			<dbset/>
			<before/>
		</outerset>
		<outerset_positions></outerset_positions>
		<input_positions></input_positions>
		<inputs>
		<$KEY /><click /><sendkeys/><jqexec/><panel/> ,vbTab
		</inputs>
		<submit_map>
		パラメータ組名 \t ボタンID : <?/><validate/><after/><dbtest/><snap/><jqexec/><checkfor/>,vbTab
		</submit_map>
		</macro_template>

		<testcase name=" panelName_submit ">
		<dbadd>uid:=\w+,pwd:=\w+,jdbc:=.*?:[tablename…]</dbadd>
		<dbadd></dbadd>
		<testcmd key=".../>  //パラメータ指定のある遷移ボタンごと
		</testcase>
	 * 　
	 * MacroActionsの作り方
	 * 前提：JsonTemplateがある。
	 * 　　①クラス名：com.ftinc.si.assist.test.web.MacroActions
	 * 　　②キー名：画面ID#モード
	 *
	 *(0)自分のオブジェクト(MacroActions)を作りマクロ化。および、二種類の入れ物（TestCase）：チェック(C_)とsubmit(S_)を作る。
	 *(1)この画面にたどり着くまでのルートを探索する。⇒ MacroActionsのコンストラクタ内で行う。
	 *(2)単純チェックの場合：C_付き
	 *　　項目ごとにマクロを生成する。
	 *　　書くマクロには、項目ごとの入力とチェックをunited_actionsに全て入れる。
	 *(2)遷移チェックの場合：S_付き
	 *　　遷移パターン一つごとにマクロを作成する。
	 *　　checkpointsがなければデフォルトの遷移。
	 *(3)テストケースにまとめる。単純チェックと遷移チェック（より重要）は分ける。
	*/


	@Override
	protected int createTestRecords(String s) {
		s_viewer.setVisible(true);

		//VBAでエスケープしていた\,や\(や\)を元に戻	していたが
		//※VBAから出る際に、戻す処理を行っているので、今は不要。

		HashMap<String, MacroActions> t_macros = new HashMap<String, MacroActions>();
		Pattern macro_pat = Pattern.compile("<macro_template\\s+([^>]*)>\\s*([\\s\\S]+?)\\s*</macro_template>"); //$NON-NLS-1$
		Matcher t_m = macro_pat.matcher(s);
		while (t_m.find()) {
			Tool.logIfDebug(null, Messages.getString("WebTestCreator.1") + t_m.group(1)); //$NON-NLS-1$
			createMacroTemplate(t_m.group(1), t_m.group(2), t_macros);
			if (t_macros.size() == 0) {
				s_viewer.setVisible(false);
				return -1;
			}
		}

		String panelName = ""; //$NON-NLS-1$
		String head = ""; //$NON-NLS-1$
		if (t_macros.size() == 1) {
			for (Entry<String, MacroActions> entry: t_macros.entrySet()) {
				String[] name_data = entry.getKey().split("_", -1); //$NON-NLS-1$
				if (name_data[1].equals("submit")) { //$NON-NLS-1$
					head = "S."; //$NON-NLS-1$
				} else {
					head = "C."; //$NON-NLS-1$
				}
				panelName = name_data[0];
			}
		} else {
			Tool.alertMSG(null, Messages.getString("WebTestCreator.9")); //$NON-NLS-1$
			s_viewer.setVisible(false);
			return -1;
		}
		ArrayList<TestCaseRecord> t_list = Tool._db().getTestCaseList(head + panelName + "%", 0, 0, Tool.group_code, Tool.version); //$NON-NLS-1$
		ArrayList<String> testCases = new ArrayList<String>();
		for (int i = 0; i < t_list.size(); i++) {
			testCases.add(t_list.get(i).getCaseName());
		}

		//TestCase生成
		Pattern case_pat1 = Pattern.compile("<testcase\\s+([^>]*)>\\s*([\\s\\S]+?)\\s*</testcase>"); //$NON-NLS-1$
		t_m = case_pat1.matcher(s);
		while (t_m.find()) {
			String t_attr = t_m.group(1);
			Tool.logIfDebug(null, Messages.getString("WebTestCreator.3") + t_attr); //$NON-NLS-1$

			String t_data = t_m.group(2);
			try {
				String t_msg = createTestCase(t_macros, t_attr, t_data, testCases);
				if (t_msg.length() > 0) {
					//エラー処理。processmodeでなくても、エラーがあればログに出力する。
					Tool.logForTesting(null, t_msg);
					s_viewer.setVisible(false);
					return -2;
				}
			} catch (Throwable e) {
				Tool.logForTesting(null, Messages.getString("WebTestCreator.30") + e.getMessage()); //$NON-NLS-1$
			}
		}
		if (testCases.size() > 0) {
			for (int i = 0; i < testCases.size(); i++) {
				try {
					Tool._db().removeCase(testCases.get(i), Tool.version);
				} catch (SQLException e) {
					Tool.alertMSG(null, Messages.getString("WebTestCreator.11") + testCases.get(i)); //$NON-NLS-1$
					s_viewer.setVisible(false);
					return 0;//一応成功
				}
			}
		}

		Tool.logIfDebug(null, "Succeeded @ " + new Date()); //$NON-NLS-1$ //$NON-NLS-2$
		setStatus("Succeeded @ " + new Date());
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			//気にしない。
		}

		s_viewer.setVisible(false);
		return 0;//成功
	}

	/*
	 * submit用マクロを作る
	 */
	private void createMacroTemplate(String attr, String s, HashMap<String, MacroActions> macros) {
		HashMap<String, String> t_map = new HashMap<String, String>();
		Matcher t_m = attribute_pat.matcher(attr);
		while (t_m.find()) {
			t_map.put(t_m.group(1), t_m.group(2));
		}

		MacroActions t_act = new MacroActions();

		final Pattern url_pat = Pattern.compile("<url>([\\s\\S]+?)</url>"); //$NON-NLS-1$
		t_m = url_pat.matcher(s);
		if (t_m.find()) {
			if (t_m.group(1).length() > 0) {
				Fson.updateByJsonPath(t_act, "$.arg_map.url", t_m.group(1)); //$NON-NLS-1$
			}
		}

		final Pattern title_pat = Pattern.compile("<window_title>([\\s\\S]+?)</window_title>"); //$NON-NLS-1$
		t_m = title_pat.matcher(s);
		if (t_m.find()) {
			if (t_m.group(1).length() > 0) {
				Fson.updateByJsonPath(t_act, "$.arg_map.title", t_m.group(1)); //$NON-NLS-1$
			}
		}

		final Pattern frame_pat = Pattern.compile("<frame>([\\s\\S]+?)</frame>"); //$NON-NLS-1$
		t_m = frame_pat.matcher(s);
		if (t_m.find()) {
			if (t_m.group(1).length() > 0) {
				Fson.updateByJsonPath(t_act, "$.arg_map.frame", t_m.group(1)); //$NON-NLS-1$
			}
		}

		final Pattern tfrom_pat = Pattern.compile("<trans_from>([\\s\\S]+?)</trans_from>"); //$NON-NLS-1$
		t_m = tfrom_pat.matcher(s);
		if (t_m.find()) {
			Fson.updateByJsonPath(t_act, "$.trans_from", "[" + t_m.group(1) + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		}

		final Pattern tto_pat = Pattern.compile("<trans_to>([\\s\\S]+?)</trans_to>"); //$NON-NLS-1$
		t_m = tto_pat.matcher(s);
		if (t_m.find()) {
			Fson.updateByJsonPath(t_act, "$.trans_to", t_m.group(1)); //$NON-NLS-1$
		}

		final Pattern mile_pat = Pattern.compile("<checkpoints>([\\s\\S]+?)</checkpoints>"); //$NON-NLS-1$
		t_m = mile_pat.matcher(s);
		if (t_m.find()) {
			Fson.updateByJsonPath(t_act, "$.checkpoints_map", t_m.group(1)); //$NON-NLS-1$
		}

		String pName = t_map.get("panelName"); //$NON-NLS-1$
		ArrayList<PageAction> act_list = new ArrayList<PageAction>();

		//input_actionsを作る。（外部コマンド/DBとコントロールの設定）
		//OuterSetの場合、空設定値は出力しない。
		final Pattern outerset_pat = Pattern.compile("<outerset>([\\s\\S]+?)</outerset>"); //$NON-NLS-1$
		t_m = outerset_pat.matcher(s);
		if (t_m.find()) {
			//vbaでエスケープしている\''を"に戻す。
			String t_str = t_m.group(1).trim().replaceAll(Pattern.quote("\\''"), "\""); //$NON-NLS-1$ //$NON-NLS-2$
			final Pattern dbset_pat = Pattern.compile("<dbset.*?/>"); //$NON-NLS-1$
			t_m = dbset_pat.matcher(t_str);
			while (t_m.find()) {
				PageAction sub_act = makeAtomAction(t_m.group(), pName, null); //macrosは不要。
				act_list.add(sub_act);
			}

			final Pattern before_pat = Pattern.compile("<before.*?/>"); //$NON-NLS-1$
			t_m = before_pat.matcher(t_str);
			while (t_m.find()) {
				PageAction sub_act = makeAtomAction(t_m.group(), pName, null); //macrosは不要。
				act_list.add(sub_act);
			}
		}

		//dbset_poslistを設定する。
		final Pattern outerset_positions_pat = Pattern.compile("<outerset_positions>([\\s\\S]+?)</ouyterset_positions>"); //$NON-NLS-1$
		t_m = outerset_positions_pat.matcher(s);
		if (t_m.find()) {
			String t_str = t_m.group(1).trim();
			String[] str_setter = t_str.split("\n", -1); //$NON-NLS-1$

			String t_json = "{"; //$NON-NLS-1$
			for (int i = 0; i < str_setter.length; i++) {
				String[] set_data = str_setter[i].split(":\t", -1); //$NON-NLS-1$
				t_json += "\"" + set_data[0] + "\":[" + set_data[1] + "],"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			}
			if (t_json.length() > 1) {
				t_json = t_json.substring(0, t_json.length() - 1) + "}"; //$NON-NLS-1$
				Fson.updateByJsonPath(t_act, "$.outerset_poslist", t_json); //$NON-NLS-1$
			}
		}

		//input_poslistを設定する。
		final Pattern input_positions_pat = Pattern.compile("<input_positions>([\\s\\S]+?)</input_positions>"); //$NON-NLS-1$
		t_m = input_positions_pat.matcher(s);
		if (t_m.find()) {
			String t_str = t_m.group(1).trim();
			String[] str_setter = t_str.split("\n", -1); //$NON-NLS-1$

			String t_json = "{"; //$NON-NLS-1$
			for (int i = 0; i < str_setter.length; i++) {
				String[] set_data = str_setter[i].split(":\t", -1); //$NON-NLS-1$
				t_json += "\"" + set_data[0] + "\":[" + set_data[1] + "],"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			}
			if (t_json.length() > 1) {
				t_json = t_json.substring(0, t_json.length() - 1) + "}"; //$NON-NLS-1$
				Fson.updateByJsonPath(t_act, "$.input_poslist", t_json); //$NON-NLS-1$
			}
		}

		ArrayList<PageAction> input_acts = new ArrayList<PageAction>();
		final Pattern inputs_pat = Pattern.compile("<inputs>([\\s\\S]+?)</inputs>"); //$NON-NLS-1$
		t_m = inputs_pat.matcher(s);
		if (t_m.find()) {
			String t_str = t_m.group(1);
			String[] str_inputs = t_str.split(",\t", -1); //$NON-NLS-1$
			for (int i = 0; i < str_inputs.length; i++) {
				ArrayList<PageAction> t_list = new ArrayList<PageAction>();
				String[] sub_acts = str_inputs[i].split("/>", 0);//タグの終わりで切る。 //$NON-NLS-1$

				//split(?,0)なので、最後の要素nullは配列にない。
				for (int j = 0; j < sub_acts.length; j++) {
					if (sub_acts[j].length() > 0) {
						PageAction sub_act = makeAtomAction(sub_acts[j], pName, null); //macrosは不要。
						t_list.add(sub_act);  //途中のnullも含める。
					}
				}

				if (t_list.size() > 0) {
					MacroActions t_macro = new MacroActions();
					t_macro.setUnitedActions(t_list);
					input_acts.add(t_macro);
				} else {
					input_acts.add(null);
				}
			}
			if (input_acts.size() > 0) {
				t_act.setInputActions(input_acts);
			}
		}

		//submit_action_mapを設定する。
		final Pattern submit_action_map_pat = Pattern.compile("<submit_map>([\\s\\S]+?)</submit_map>"); //$NON-NLS-1$
		t_m = submit_action_map_pat.matcher(s);
		if (t_m.find()) {
			HashMap<String, ArrayList<PageAction>> submit_map = new HashMap<String, ArrayList<PageAction>>();
			//vbaでエスケープしている\''を"に戻す。
			String t_str = t_m.group(1).trim();

			String[] str_setter = t_str.split("\n", -1); //$NON-NLS-1$
			for (int i = 0; i < str_setter.length; i++) {  //各遷移ボタンごとにアクションを作る
				String[] set_data = str_setter[i].split(",\t", -1); //$NON-NLS-1$
				for (int j = 0; j < set_data.length; j++) {
					String[] temp = set_data[j].split(":\t", -1); //$NON-NLS-1$
					String t_key = temp[0].trim();
					String[] action_data = temp[1].trim().split("/>", 0); // "/>"の後のnull要素は廃棄のはずだが・・・。 //$NON-NLS-1$

					ArrayList<PageAction> t_list = new ArrayList<PageAction>();
					for (int k = 0; k < action_data.length; k++) {
						PageAction sub_act = makeAtomAction(action_data[k], pName, macros);
						if (sub_act != null) {
							t_list.add(sub_act);
						} else {
							//末尾ではないのに、何かおかしい。
							Tool.logForTesting(null, "src = " + temp[1] + "\nelm @k="+ Integer.toString(k) + Messages.getString("WebTestCreator.54")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
						}
					}
					if (t_list.size() > 0) {
						submit_map.put(t_key, t_list);
					}
				}
			}
			if (submit_map.size() > 0) {
				t_act.setSubmitMap(submit_map);
			}
		}

		//★★submit型かcheck型かは、ここで指定する。
		String t_options = t_map.get("type"); //$NON-NLS-1$
		if (t_options == null) {
			t_options = ""; //$NON-NLS-1$
		}
		t_act.options = t_options;

		//t_actをJsonTemplateに登録する。
		macros.put(pName + t_options, t_act);
		try {
			setStatus("Saving JsonTemplatee=" + pName + t_options);

			Tool.saveJSONfromObject(pName + t_options, t_act, "(arg_map|options|trans_|action|poslist|checkpoints)"); //$NON-NLS-1$
		} catch (SQLException e) {
			Tool.logForTesting(e, Messages.getString("WebTestCreator.59") + pName + "."); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	private static Pattern attribute_pat = Pattern.compile("\\s*([^\\s=]+)\\s*=\\s*\"([^\"]+?)\""); //$NON-NLS-1$

	//s: PageActionを構成すべき文字列
	//p_map：その上層の属性マップ
	protected PageAction makeAtomAction(String s, String panelName, HashMap<String, MacroActions> macros) {
		HashMap<String, String> t_map = new HashMap<String, String>();
		Matcher t_m = attribute_pat.matcher(s);
		while (t_m.find()) {
			String t_str = t_m.group(2);
			t_map.put(t_m.group(1), t_str);
		}
		if (t_map.containsKey("$VAR")) { //$NON-NLS-1$
			t_map.put("$VAR", t_map.get("$VAR").replaceAll(Pattern.quote("\\''"), "\"")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
		}

		PageAction t_act = null;
		if (s.startsWith("<$KEY")) { //$NON-NLS-1$
			t_act = new IndexSetter();
		} else if (s.startsWith("<click")) { //$NON-NLS-1$
			t_act = new Click();
		} else if (s.startsWith("<hover")) { //$NON-NLS-1$
			t_act = new Hover();
		} else if (s.startsWith("<snapshot")) { //$NON-NLS-1$
			t_act = new Snap();
		} else if (s.startsWith("<sendkeys")) { //$NON-NLS-1$
			t_act = new SendKeys();
		} else if (s.startsWith("<verify")) { //$NON-NLS-1$
			t_act = new Verify();
		} else if (s.startsWith("<hand")) { //$NON-NLS-1$
			t_act = new UseHand();
		} else if (s.startsWith("<startClock")) { //$NON-NLS-1$
			t_act = new Clock();
		} else if (s.startsWith("<endClock")) { //$NON-NLS-1$
			t_act = new Clock();
			t_act.options = "end";
		} else if (s.startsWith("<sleep")) { //$NON-NLS-1$
			t_act = new Sleep();
		} else if (s.startsWith("<drag")) { //$NON-NLS-1$
			t_act = new Drag();
		} else if (s.startsWith("<drop")) { //$NON-NLS-1$
			t_act = new Drop();
		} else if (s.startsWith("<checkfor")) { //$NON-NLS-1$
			t_act = new CheckFor();
		} else if (s.startsWith("<prompt")) { //$NON-NLS-1$
			t_act = new Prompt();
		} else if (s.startsWith("<alert")) { //$NON-NLS-1$
			t_act = new CheckFor();
			t_act.options = "Alert"; //$NON-NLS-1$
		} else if (s.startsWith("<validate")) { //$NON-NLS-1$
			//画面やFrameの確認。
			MacroActions x_act = new MacroActions();

			ArrayList<PageAction> t_list = new ArrayList<PageAction>();
			if (t_map.containsKey("title")) { //$NON-NLS-1$
				CheckFor t_ch = new CheckFor();
				t_ch.options = "ToWindow"; //$NON-NLS-1$
				Fson.updateByJsonPath(t_ch, "$.arg_map.title", t_map.get("title")); //$NON-NLS-1$ //$NON-NLS-2$
				t_list.add(t_ch);
			} else if (t_map.containsKey("frame")) { //$NON-NLS-1$
				ToFrame t_ch = new ToFrame();
				String str_id = t_map.get("frame");
				if (str_id.startsWith("src=")) {
					str_id = str_id.substring(4);
					Fson.updateByJsonPath(t_ch, "$.arg_map.src", str_id); //$NON-NLS-1$ //$NON-NLS-2$
				} else if (str_id.startsWith("title=")) {
					str_id = str_id.substring(6);
					Fson.updateByJsonPath(t_ch, "$.arg_map.title", str_id); //$NON-NLS-1$ //$NON-NLS-2$
				} else if (str_id.startsWith("xpath=")) {
					str_id = str_id.substring(6);
					Fson.updateByJsonPath(t_ch, "$.arg_map.xpath", str_id); //$NON-NLS-1$ //$NON-NLS-2$
				} else if (str_id.startsWith("name=")) {
					str_id = str_id.substring(5);
					Fson.updateByJsonPath(t_ch, "$.arg_map.name", str_id); //$NON-NLS-1$ //$NON-NLS-2$
				}
				t_list.add(t_ch);
			}
			x_act.setUnitedActions(t_list);
			return x_act;
		} else if (s.startsWith("<panel")) { //$NON-NLS-1$
			//画面を指定して、自動遷移を行う。ポップアップ画面の自動遷移など。
			String p_name = t_map.get("id"); //$NON-NLS-1$
			if (p_name == null) {
				p_name = panelName;
			}

			String btn_name = t_map.get("action"); //$NON-NLS-1$
			String param_name = t_map.get("param"); //$NON-NLS-1$

			MacroActions x_act = new MacroActions();
			Fson.updateByJsonPath(x_act, "$.key4actions", p_name + "\t" + param_name + "\t" + btn_name); //画面ID＋モード \t パラメータ組名 \ ボタン //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			return x_act;
		} else if (s.startsWith("<jqexec")) { //$NON-NLS-1$
			JQExec x_act = new JQExec();

			if (t_map.containsKey("script")) { //$NON-NLS-1$
				String t_script = t_map.get("script"); //$NON-NLS-1$

				Fson.updateByJsonPath(x_act, "$.arg_map.script", "(java.lang.String)" + t_script); //$NON-NLS-1$ //$NON-NLS-2$

				String t_args = t_map.get("args"); //$NON-NLS-1$
				String[] arg_array = t_args.split(",\t", 0); //$NON-NLS-1$
				ArrayList<String> t_list = new ArrayList<String>();
				for (int i = 0; i < arg_array.length; i++) {
					t_list.add(arg_array[i]);
				}

				Fson.updateByJsonPath(x_act, "$.arg_map.arg_values", Tool.getJSONfromObject(t_list)); //$NON-NLS-1$
				Fson.updateByJsonPath(x_act, "$.arg_map.xpath", t_map.get("xpath")); //$NON-NLS-1$ //$NON-NLS-2$
			} else {
				Tool.alertMSG(null, "No Javascript."); //$NON-NLS-1$
			}

			if (t_map.containsKey("value")) { //$NON-NLS-1$
				Fson.updateByJsonPath(x_act, "$.arg_map.value", t_map.get("value")); //$NON-NLS-1$ //$NON-NLS-2$
			}
			return x_act;
		} else if (s.startsWith("<dbtest")) { //$NON-NLS-1$
			t_act = new DBGetter();
			Fson.updateByJsonPath(t_act, "$.arg_map.jpath", "(java.lang.String)dbtest " + t_map.get("jpath")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

			Fson.updateByJsonPath(t_act, "$.arg_map.value", "(java.lang.String)" + t_map.get("value")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			return t_act;
		} else if (s.startsWith("<dbset")) { //$NON-NLS-1$
			t_act = new DBSetter();
			Fson.updateByJsonPath(t_act, "$.arg_map.jpath", "(java.lang.String)dbset " + t_map.get("jpath")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$

			Fson.updateByJsonPath(t_act, "$.arg_map.value", "(java.util.HashMap)" + t_map.get("setter")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			return t_act;
		} else if (s.startsWith("<after")) { //$NON-NLS-1$
			t_act = new AfterAction();
			ArrayList<String> t_arg = new ArrayList<String>();
			t_arg.add("(java.lang.String)" + t_map.get("path")); //$NON-NLS-1$ //$NON-NLS-2$
			t_arg.add("(java.lang.String)" + t_map.get("cmd")); //$NON-NLS-1$ //$NON-NLS-2$
			Fson.updateByJsonPath(t_act, "$.arg_map.value", Tool.getJSONfromObject(t_arg)); //$NON-NLS-1$
			return t_act;
		} else if (s.startsWith("<before")) { //$NON-NLS-1$
			t_act = new BeforeAction();
			ArrayList<String> t_arg = new ArrayList<String>();
			t_arg.add("(java.lang.String)" + t_map.get("path")); //$NON-NLS-1$ //$NON-NLS-2$
			t_arg.add("(java.lang.String)" + t_map.get("cmd")); //$NON-NLS-1$ //$NON-NLS-2$
			Fson.updateByJsonPath(t_act, "$.arg_map.value", Tool.getJSONfromObject(t_arg)); //$NON-NLS-1$
			return t_act;
		} else if (s.matches("^<[\\w\\.]+\\.[A^Z]\\w+\\s.*$")) { //$NON-NLS-1$
			//PageActionのpluginである。
			String c_name = s.replaceFirst("^<([\\w\\.]+\\.[A^Z]\\w+)\\s.*$", "$1"); //$NON-NLS-1$ //$NON-NLS-2$
			Class<?> t_c;
			try {
				t_c = Tool.forName(c_name);
				t_act = (PageAction)Tool.newObject(t_c, new Class<?>[] {}, new Object[]{});
			} catch (ClassNotFoundException | SecurityException | NoSuchMethodException | IllegalArgumentException |
					InstantiationException | IllegalAccessException | InvocationTargetException e) {
				Tool.logIfDebug(e, null);
			}
		}
		//ここまででreturnがない場合、属性を一気に埋め込む。
		for (Entry<String, String> entry: t_map.entrySet()) {
			Fson.updateByJsonPath(t_act, "$.arg_map." + entry.getKey(), entry.getValue()); //$NON-NLS-1$
		}
		if (t_act == null) {
			Tool.logIfDebug(null, Messages.getString("WebTestCreator.41") + s); //$NON-NLS-1$
		}
		return t_act;
	}

	//第一引数はマクロ（Template用）のほぼ完成形。Historyを作るため。
	private String createTestCase(HashMap<String, MacroActions> xmacros, String attr, String s, ArrayList<String> testCases) {
		String t_message = ""; //$NON-NLS-1$

		HashMap<String, String> t_map = new HashMap<String, String>();
		Matcher t_m = attribute_pat.matcher(attr);
		while (t_m.find()) {
			t_map.put(t_m.group(1), t_m.group(2));
		}

		HashMap<String, Object> v_map = new HashMap<String, Object>();
		String[] init_cmds;

		//DBの接続定義は、ここで抽出し、createSubmitCaseで埋め込む。
		final Pattern dbadd_pat = Pattern.compile("<dbadd>([\\s\\S]+?)</dbadd>"); //$NON-NLS-1$
		t_m = dbadd_pat.matcher(s);
		if (t_m.find()) {
			//書式 uid:=\w+,pwd:=\w+,jdbc:=.*?:[tablename…]
			init_cmds = t_m.group(1).split("\n", -1); //$NON-NLS-1$

			//DBの接続定義の埋め込み.コマンド毎に埋め込むが、最初のコマンドの設定が使用される。
			for (int i = 0; i < init_cmds.length; i++) {
				//	uid:=\w+,pwd:=\w+,jdbc:=.*?:[tablename…]
				//	↓
				//	dbAdd “[?(@.uid=’ユーザID’)][?(@ .pwd=’パスワード’)] [?(@.jdbc=’接続文字列’)]”: [“テーブル名”...]

				String v_key = "dbadd "; //$NON-NLS-1$
				final Pattern db_pat1 = Pattern.compile("(\\w+):=[\"']?([^,\\[=]+?)[\\\"']?(,|:\\[)"); //$NON-NLS-1$
				t_m = db_pat1.matcher(init_cmds[i]);
				while (t_m.find()) {
					String t_key = t_m.group(1);
					String t_val = t_m.group(2);

					v_key += "[?(@." + t_key + "='" + t_val + "')]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
				}

				final Pattern db_pat2 = Pattern.compile(":(\\[.*)\\s*$"); //$NON-NLS-1$
				t_m = db_pat2.matcher(init_cmds[i]);
				if (t_m.find()) {
					v_map.put(v_key, Tool.getObjectfromJSON(ArrayList.class, t_m.group(1)));
				}
			}
		}

		//コマンドに必要な要素は切り出す。 "<testcmd key=""" & t_key & """/>"
		ArrayList<String> cmd_datalist = new ArrayList<String>();

		final Pattern cmd_pat = Pattern.compile("<testcmd\\s+key=\"([^\"]+)\"\\s*/>"); //$NON-NLS-1$
		t_m = cmd_pat.matcher(s);
		while (t_m.find()) {
			//キーkey4actionsの値を切り出す
			cmd_datalist.add(t_m.group(1).trim());
		}

		//TestCaseを作る。コマンドの数(cmd_datalist.size())の二倍。
		//初期の名前は submit型: panelID#mode _\t ボタンのitemID
		//              check型: panelID#mode
		String[] casename_parts = t_map.get("name").split("_\t", -1); //$NON-NLS-1$ //$NON-NLS-2$
		String caseName = casename_parts[0];	 //$NON-NLS-1$

		if (t_map.containsKey("type")) { //$NON-NLS-1$
			//submit:  panel#mode_\t_submit
			//check:  panel#mode_check
			if (t_map.get("type").toString().equals("_submit")) { //$NON-NLS-1$ //$NON-NLS-2$
				caseName = "S." + caseName; //$NON-NLS-1$
			} else {
				caseName = "C." + caseName; //$NON-NLS-1$
			}
		}
		//submit型：S.panelID#mode.buttonID.2.transName
		//check型：C.panelID#mode
		if (casename_parts.length == 2) {
			caseName += "." + casename_parts[1]; //$NON-NLS-1$
		}

		HashMap<Integer, TestCaseRecord> t_casemap = lookupTestCaseWithLength(caseName, cmd_datalist.size());
		if (t_casemap == null) {
			return Messages.getString("WebTestCreator.144"); //$NON-NLS-1$
		}

		//入れ物を作る。
		HashMap<Integer, TestCommandRecord> t_cmds = new HashMap<Integer, TestCommandRecord>();
		HashMap<String, ObjectRecord> t_objs = new HashMap<String, ObjectRecord>();
		HashMap<Integer, AssertRecord> t_asserts = new HashMap<Integer, AssertRecord>();

		int t_id = 0;
		TestCaseRecord t_case = t_casemap.get(0);//0は既存流用、1は新規生成。どちらか一方が存在する。
		if (t_case == null) {
			t_case = t_casemap.get(1);//既存に収まらなければ新規作成。
		}
		t_case.changed();

		String xoptions = t_map.get("type"); //$NON-NLS-1$
		if (xoptions == null) {
			xoptions = ""; //$NON-NLS-1$
		}

		//DB接続の準備コマンド
		if (v_map.size() != 0) {
			//入れ物を生成する。
			TestCommandRecord t_cmd = new TestCommandRecord(2);
			t_cmd.id = t_case.begin + t_id;
			t_cmd.className = "com.ftinc.si.assist.run.VCentral"; //$NON-NLS-1$
			t_cmd.enable = true;
			t_cmd.methodName = "prepare"; //$NON-NLS-1$
			t_cmd.subject = "null"; //$NON-NLS-1$
			t_cmd.setArgTypes("[[java.util.HashMap,$1]]"); //$NON-NLS-1$
			t_cmd.argStatus = "[true,true,true]"; //$NON-NLS-1$
			t_cmd.returnType = "void"; //$NON-NLS-1$
			t_cmd.testCase = t_case.name;
			t_cmds.put(t_id, t_cmd);

			//$1
			ObjectRecord t_obj = new ObjectRecord(2);
			t_obj.className = "java.util.HashMap"; //$NON-NLS-1$
			t_obj.id = Integer.toString(t_cmd.id) + "$1"; //$NON-NLS-1$
			t_obj.stack = "$1"; //$NON-NLS-1$
			t_obj.testCase = t_case.name;
			t_obj.testID = t_cmd.id;
			t_obj.jSON = Tool.getJSONfromObject(v_map);  //DBAddの埋め込み
			t_objs.put(t_obj.id, t_obj);

			//Assert
			AssertRecord t_ast = new AssertRecord(2);
			t_ast.enable = false;
			t_ast.id = t_cmd.id;
			t_asserts.put(t_cmd.id, t_ast);

			t_id++;
		}

		//browserが設定されていれば、HashMap型のJsonになっているはず。
		String browser_json = "null"; //$NON-NLS-1$
		final Pattern browser_pat = Pattern.compile("<browser>[\\s\\S]*?</browser>"); //$NON-NLS-1$
		t_m = browser_pat.matcher(s);
		if (t_m.find()) {
			browser_json = t_m.group(1);
		}

		//テストコマンド本体の実行
		//入れ物にオブジェクトを格納する。
		for (int i = 0; i < cmd_datalist.size(); i++) {
			//要素=パラメータ組名 \t ボタンID
			//コマンド
			TestCommandRecord t_cmd = new TestCommandRecord(2);
			t_cmd.id = t_case.begin + t_id;
			t_cmd.className = "com.ftinc.si.assist.test.web.PagePlayer"; //$NON-NLS-1$
			t_cmd.enable = true;
			t_cmd.methodName = "play"; //$NON-NLS-1$
			t_cmd.subject = "$0"; //$NON-NLS-1$
			t_cmd.setArgTypes("[[java.util.HashMap,$1]]"); //$NON-NLS-1$
			t_cmd.argStatus = "[true,true,true]"; //$0,$1, assertの３つが完全である。 //$NON-NLS-1$
			t_cmd.returnType = "java.util.ArrayList"; //$NON-NLS-1$
			t_cmd.testCase = t_case.name;

			t_cmds.put(t_id, t_cmd);

			//$0
			ObjectRecord t_obj0 = new ObjectRecord(2, t_case.name, t_cmd.id, 0, PagePlayer.class.getName(), "$0"); //$NON-NLS-1$

			//キー、Historyを登録する。
			//この作り方>> ExecLayer.TARGET
			MacroActions t_macro = xmacros.get(casename_parts[0] + xoptions);
			if (t_macro == null) {
				JSONRecord t_jdata = JSONRecord.getJSONRec(MacroActions.class.getName(), casename_parts[0] + xoptions);
				if (t_jdata == null) {
					return Messages.getString("WebTestCreator.164") + casename_parts[0] + Messages.getString("WebTestCreator.165"); //$NON-NLS-1$ //$NON-NLS-2$
				}
				t_macro = (MacroActions)Tool.getObjectfromJSON(MacroActions.class, t_jdata.content);
			}

			//Historyの計算（コンストラクタで実施）とJsonTemplateへの保存。Historyは通過点（key4action）のリスト。
			MacroActions r_macro = new MacroActions(cmd_datalist.get(i), t_macro, xoptions); //$NON-NLS-1$
			PagePlayer t_player = new PagePlayer();
			t_player.actions = new ArrayList<PageAction>();
			t_player.actions.add(r_macro);
			t_player.actions.add(new ResetBrowser());//クッキーの除去などブラウザをリセットする。

			t_obj0.setJSON(Tool.getJSONfromObject(t_player, "(^actions$|key4actions|arg_map|history|options|path|title|value)"));//必要な属性のみ保存する。 //$NON-NLS-1$
			t_objs.put(t_obj0.id, t_obj0);

			//$1
			ObjectRecord t_obj1 = new ObjectRecord(2, t_case.name, t_cmd.id, 1, "java.util.HashMap", "$1"); //$NON-NLS-1$ //$NON-NLS-2$
			t_obj1.setJSON(browser_json);
			t_objs.put(t_obj1.id, t_obj1);
			t_id++;
		}

		try {
			setStatus("Saving TestSuite=" + t_case.name);

			ArrayList<Record> removings = null;
			if (t_casemap.containsKey(0)) {//クリアする必要がある。
				//既存テストケースの配下を削除
				t_message = Messages.getString("WebTestCreator.0") + t_case.name + "."; //$NON-NLS-1$ //$NON-NLS-2$

				Tool._db().clearCase(t_case, true);
			} else if (t_casemap.containsKey(-1)) {//削除する必要がある。
				removings = new ArrayList<Record>();
				removings.add(t_casemap.get(-1));

				Tool._db().clearCase(t_case, true);
			}
			t_message = Messages.getString("WebTestCreator.184") + t_case.name + "."; //$NON-NLS-1$ //$NON-NLS-2$

			//TestCaseの更新。配下は新規として登録する。
			if (Tool._db().saveTestCase(t_case, removings, t_cmds, t_objs, t_asserts)) {
				t_message = "";
			}

			if (testCases.contains(t_case.name)) {
				//処理済みを削除する。
				testCases.remove(t_case.name);
			}
		} catch (SQLException e) {
			t_message += "\nException=" + e.getMessage(); //$NON-NLS-1$
		}
		return t_message;
	}

	public static void setStatus(String msg) {
		if (s_viewer.isVisible()) {
			s_viewer.update(msg);
		}
	}
}
