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

import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javassist.CtClass;
import javassist.CtMethod;
import javassist.NotFoundException;

import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.text.Caret;

import com.ftinc.si.assist.run.Messages;
import com.ftinc.si.assist.test.ClassOutLine;
import com.ftinc.si.assist.test.CodeProcessor;
import com.ftinc.si.assist.test.FakeMethodRecord;
import com.ftinc.si.assist.test.TestCommandRecord;
import com.ftinc.si.assist.test.Tool;

public class MethodEditor extends JDialog {
	public static JDialog s_owner;
	
	private JTextField className;
	private JTextField methodName;
	private JTextArea argTypes;
	private JTextArea textSource;
	private JTextArea fields;//このメソッドで作られた仮想メンバ変数を表示する。
	private JTextArea resultText;
	private String returntype;
	public boolean is_OK = false;
	private HashMap<String, Class<?>> m_cmap = null;
	private String m_checked;
	private boolean m_removing = false;
	private boolean is_fake = false;
	
	public MethodEditor(JDialog owner, String cname, String mname, String[] atypes, String src) {
		super(owner, true);
		
		setTitle(Messages.getString("MethodEditorInBrowser.0")); //$NON-NLS-1$
		getContentPane().setLayout(null);
		if (owner != null) {
			setBounds(owner.getX() + 100, owner.getY() + 100, 1000, 485);
		} else {
			setBounds(100, 100, 1000, 485);
		}
		
		JLabel lblNewLabel = new JLabel(Messages.getString("MethodEditorInBrowser.1")); //$NON-NLS-1$
		lblNewLabel.setBounds(12, 104, 162, 13);
		getContentPane().add(lblNewLabel);
		
		JLabel lblNewLabel_1 = new JLabel(Messages.getString("MethodEditorInBrowser.2")); //$NON-NLS-1$
		lblNewLabel_1.setBounds(12, 165, 132, 13);
		getContentPane().add(lblNewLabel_1);
		
		final JButton btnUpdate = new JButton(Messages.getString("MethodEditorInBrowser.3")); //$NON-NLS-1$
		btnUpdate.setBounds(406, 415, 91, 21);
		btnUpdate.setEnabled(!TestCaseEditor.unableMode);
		getContentPane().add(btnUpdate);
		
		JButton btnCancel = new JButton(Messages.getString("MethodEditorInBrowser.4")); //$NON-NLS-1$
		btnCancel.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				className.setText(""); //$NON-NLS-1$
				setVisible(false);
			}
		});
		btnCancel.setBounds(556, 415, 91, 21);
		getContentPane().add(btnCancel);	
		
		className = new JTextField();
		className.setBounds(12, 75, 267, 19);
		getContentPane().add(className);
		className.setColumns(10);
		if (cname != null) {
			className.setText(cname);
		}
		
		JButton btnFindClassmethod = new JButton(Messages.getString("MethodEditorInBrowser.5")); //$NON-NLS-1$

		btnFindClassmethod.setBounds(12, 10, 138, 21);
		getContentPane().add(btnFindClassmethod);
		
		JLabel lblClassname = new JLabel(Messages.getString("MethodEditorInBrowser.6")); //$NON-NLS-1$
		lblClassname.setBounds(12, 52, 98, 13);
		getContentPane().add(lblClassname);
		
		methodName = new JTextField();
		methodName.addKeyListener(new KeyAdapter() {
			@Override
			public void keyTyped(KeyEvent e) {
				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
					if (textSource.getText().length() > 0) {
						int option = JOptionPane.showConfirmDialog(null, Messages.getString("MethodEditor.0"), Messages.getString("MethodEditor.22"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
						if (option == JOptionPane.CANCEL_OPTION){
							return;
						}
					}

					try {
						CtMethod t_m = Tool.getCtMethod(null, className.getText(), methodName.getText(), null);
						if (t_m != null) {
							CtClass[] atypes = t_m.getParameterTypes();
							
							returntype = t_m.getReturnType().getName();
							
							String t_str = "$0=" + className.getText(); //$NON-NLS-1$
							for (int i = 0; i < atypes.length; i++) {
								t_str += "\n$" + Integer.toString(i + 1) + "=" + atypes[i].getName(); //$NON-NLS-1$ //$NON-NLS-2$
							}
							argTypes.setText(t_str);
						}
					} catch (NotFoundException e1) {
						Tool.alertMSG(null,  e1.getMessage());
					}
				}

			}
		});
		methodName.setBounds(12, 127, 273, 19);
		getContentPane().add(methodName);
		methodName.setColumns(10);
		
		JLabel lblDeclaredFields = new JLabel(Messages.getString("MethodEditorInBrowser.7")); //$NON-NLS-1$
		lblDeclaredFields.setBounds(12, 315, 113, 13);
		getContentPane().add(lblDeclaredFields);
		
		JScrollPane scrollPane_2 = new JScrollPane();
		scrollPane_2.setBounds(12, 333, 273, 91);
		getContentPane().add(scrollPane_2);
		
		fields = new JTextArea();
		scrollPane_2.setViewportView(fields);
		
		JLabel lblSource = new JLabel(Messages.getString("MethodEditorInBrowser.8")); //$NON-NLS-1$
		lblSource.setBounds(297, 14, 50, 13);
		getContentPane().add(lblSource);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(297, 37, 675, 185);
		getContentPane().add(scrollPane_1);
		
		textSource = new JTextArea();
		textSource.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//右クリックならポップアップを開く。
				if(SwingUtilities.isRightMouseButton(e)){
					showPopup(e);
				}
			}
		});
		textSource.addMouseWheelListener(new MouseWheelListener() {
			public void mouseWheelMoved(MouseWheelEvent e) {
				//マウスホイールの操作により、フォントサイズを変える。
				Font t_font = Tool.resizeFont(e, textSource.getFont());
				if (t_font != null) {
					textSource.setFont(t_font);
				}
			}
		});
		textSource.setFont(new Font("MS UI Gothic", Font.PLAIN, 14));
		textSource.addKeyListener(new KeyAdapter() {
			@Override
			public void keyTyped(KeyEvent e) {
				if (e.getKeyChar() == '.') {
					popupCandidate(e);
				}
			}
		});
		textSource.setEditable(!TestCaseEditor.unableMode);
		scrollPane_1.setViewportView(textSource);
		
		JButton btnCompile = new JButton(Messages.getString("MethodEditorInBrowser.10")); //$NON-NLS-1$
		btnCompile.setBounds(822, 10, 132, 21);
		getContentPane().add(btnCompile);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(297, 250, 675, 155);
		getContentPane().add(scrollPane);
		
		//ソースをコンパイルした結果
		resultText = new JTextArea();
		scrollPane.setViewportView(resultText);
		
		JLabel lblResult = new JLabel(Messages.getString("MethodEditorInBrowser.11")); //$NON-NLS-1$
		lblResult.setBounds(297, 232, 132, 13);
		getContentPane().add(lblResult);
		
		JScrollPane scrollPane_3 = new JScrollPane();
		scrollPane_3.setBounds(12, 188, 273, 117);
		getContentPane().add(scrollPane_3);
		
		argTypes = new JTextArea();
		scrollPane_3.setViewportView(argTypes);
		
		final JButton btnRemove = new JButton(Messages.getString("MethodEditor.btnRemove.text")); //$NON-NLS-1$
		btnRemove.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (!btnRemove.isEnabled()) {
					return;
				}
				
				int opt = JOptionPane.showConfirmDialog(null, Messages.getString("MethodEditor.1"), Messages.getString("MethodEditor.3"), JOptionPane.YES_NO_OPTION); //$NON-NLS-1$ //$NON-NLS-2$
				
				if (opt == JOptionPane.YES_OPTION) {
					m_removing = true;
				}
				setVisible(false);
			}
		});
		btnRemove.setBounds(881, 415, 91, 21);
		btnRemove.setEnabled(!TestCaseEditor.unableMode);
		getContentPane().add(btnRemove);
		
		//TestCaseBrowserから直接呼ばれた時、削除可能にする。
		if (s_owner == null) {
			btnRemove.setEnabled(false);
		}
		
		btnCompile.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				check();
			}
		});

		btnFindClassmethod.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (textSource.getText().length() > 0) {
					int option = JOptionPane.showConfirmDialog(null, Messages.getString("MethodEditor.2"), Messages.getString("MethodEditor.22"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					if (option == JOptionPane.CANCEL_OPTION){
						return;
					}
				}
				
				//ClassReadDialogを起動する。
				ClassReadDialog t_dial = new ClassReadDialog((JDialog)Tool.getParent((Component)e.getSource(), "JDialog"), Tool.target_dirs, null); //$NON-NLS-1$
				if (is_fake) {
					t_dial.setClassName("test.impl." + className.getText()); //$NON-NLS-1$
				} else {
					t_dial.setClassName(className.getText());
				}
				t_dial.setVisible(true);
				
				if (t_dial.methodName != null && t_dial.methodName.matches("^.*(final|abstract).*$")) {//final,abstractは除外。編集できないので。 //$NON-NLS-1$
					Tool.alertMSG(null, Messages.getString("MethodEditor.5")); //$NON-NLS-1$
				} else if (t_dial.className != null && t_dial.methodName != null) {
					String cname = t_dial.className;
					if (cname.matches("^test\\.fake\\..*|^test\\.impl\\..*")) { //$NON-NLS-1$
						className.setText(cname.substring(10));//fake部分を削除
						is_fake = true;
					} else {
						className.setText(cname);
						is_fake = false;
					}
					String mname = t_dial.methodName;
					if (mname.matches("^test\\.fake\\..*|^test\\.impl\\..*")) { //$NON-NLS-1$
						methodName.setText(mname.substring(10));//fake部分を削除
					} else {
						methodName.setText(mname);
					}
					if (cname.equals(mname) && s_owner == null) {
						//m_ownerがnullなのはFakeの編集なのでコンストラクタは対象外。
						Tool.alertMSG(null, Messages.getString("MethodEditor.4")); //$NON-NLS-1$
						return;
					}
					
					methodName.setText(t_dial.methodName);
					
					String atype = t_dial.getArgTypes();//$_=xx,$0=xx,...

					//返却値を求める。
					Pattern t_pat = Pattern.compile("\\$_=(.*?),"); //$NON-NLS-1$
					Matcher t_m = t_pat.matcher(atype);
					if (t_m.find()) {
						returntype = t_m.group(1);
					}
					
					atype = atype.replaceFirst("\\$_=.*?,\n", ""); //$NON-NLS-1$ //$NON-NLS-2$
					argTypes.setText(atype.replaceAll(",", "")); //$NON-NLS-1$ //$NON-NLS-2$
					
					//FakeのargTypesに書式を合わせる。
					atype = atype.replaceAll("\\$_=[\\w\\.\\[\\]]*,?", ""); //$NON-NLS-1$ //$NON-NLS-2$
					atype = atype.replaceAll("\\$0=[\\w\\.\\[\\]]+,?", ""); //$NON-NLS-1$ //$NON-NLS-2$
					atype = atype.replaceAll("\\$[0-9]+=", ""); //$NON-NLS-1$ //$NON-NLS-2$
					atype = atype.replaceAll("\\s", ""); //$NON-NLS-1$ //$NON-NLS-2$
					String[] alist = (String[])Tool.getObjectfromJSON(String[].class, "[" + atype + "]"); //$NON-NLS-1$ //$NON-NLS-2$
					
					//static, verposを落とし、メソッド名のみ取り出すためTestCommandRecordを利用する。
					TestCommandRecord t_cmd = new TestCommandRecord(0);
					t_cmd.methodName = methodName.getText();
					
					FakeMethodRecord t_f = new FakeMethodRecord(0);
					t_f = Tool._db().getMethodSource(className.getText(), t_cmd.getMethodName(), alist, Tool.version);
					if (t_f != null) {
						textSource.setText(t_f.source);
						check();
					} else {
						textSource.setText(""); //$NON-NLS-1$
						resultText.setText(""); //$NON-NLS-1$
					}
				}
			}
		});

		btnUpdate.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (!btnUpdate.isEnabled()) {
					return;
				}

				//targetObjの構文の返却型チェック。
				if (isSyntaxOK()) {
					is_OK = true;
					
					setVisible(false);
				} else {
					int option = JOptionPane.showConfirmDialog(null, m_checked + Messages.getString("MethodEditor.21"), Messages.getString("MethodEditor.22"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
					if (option == JOptionPane.YES_OPTION){
						is_OK = true;
						setVisible(false);
					}
				}
			}
		});
		
		init(cname, mname, atypes, src);
	}
	
	private boolean isSyntaxOK() {
		//完全に未入力の場合、黙ってfalse。理由は明らか。
		if (textSource.getText().length() == 0) {
			return false;
		}
		
		if (m_cmap == null) {
			m_cmap  = new HashMap<String, Class<?>>();
			try {
				String t_args = argTypes.getText();
				
				//引数に何かをキャストして代入している箇所から型情報を取得。上書きする。
				final Pattern t_pat = Pattern.compile("\\$[1-9][0-9]*=([\\w\\.]+?)$"); //$NON-NLS-1$
				Matcher t_m = t_pat.matcher(t_args);
				int i = 1;
				while(t_m.find()) {
					String cname = t_m.group(1);
					m_cmap.put("$" + Integer.toString(i), Tool.forName(Tool.primitiveToFQN(cname))); //$NON-NLS-1$
					i++;
				}
				
				//static, verposを落とす
				TestCommandRecord t_rec = new TestCommandRecord(0);
				t_rec.methodName = methodName.getText();
				
				//メソッドの返す型を取得する。
				Field[] f_list = Tool.forName(className.getText()).getDeclaredFields();
				for (int j = 0; j < f_list.length; j++) {
					f_list[j].setAccessible(true);;
					if (t_rec.isStatic()) {//staticの場合、static変数のみ見える。
						if (Modifier.isStatic(f_list[j].getModifiers())) {
							m_cmap.put(f_list[j].getName(), f_list[j].getType());
						}
					} else {
						m_cmap.put(f_list[j].getName(), f_list[j].getType());
					}
				}
			} catch (Exception e) {
				Tool.logIfDebug(e, "@MethodEditor#isSyntaxOK"); //$NON-NLS-1$
				return false;
			}
		}

		//仮想メンバ変数が複数のクラスで登録されている場合
		ClassOutLine t_clo = Tool.getClassOutLine(className.getText());
		if (t_clo.getDeclaredFields(getFakeMethodRecord()) == null) {
			m_checked = Messages.getString("MethodEditor.26"); //$NON-NLS-1$
			return false;
		}

		//返却値の整合性チェック。マクロを展開してからチェックする。
		try {
			String t_src = CodeProcessor.preProcessMain(null, null, textSource.getText());
			m_checked = CodeProcessor.checkReturnType(returntype, t_src, m_cmap);
			return (m_checked == null);//nullのとき成功
		} catch (SecurityException e) {
			Tool.logIfDebug(e, "@MethodEditor#isSyntaxOK >>" + m_checked); //$NON-NLS-1$
		}
		return false;
	}

	private void init(String cname, String mname, String[] atypes, String src) {
		if (src == null && cname != null && mname != null) {
			TestCommandRecord t_rec = new TestCommandRecord(0);
			t_rec.methodName = mname;

			FakeMethodRecord t_f = Tool._db().getMethodSource(cname, t_rec.getMethodName(), atypes, Tool.version);
			src = t_f.source;
		}
		if (src != null) {
			//ソース編集に余計な括弧を取る。
			if (src.startsWith("{")) { //$NON-NLS-1$
				src = src.substring(1, src.length() -1);
			}
			
			textSource.setText(src);
			methodName.setText(mname);
			className.setText(cname);
			
			String t_str = ""; //$NON-NLS-1$

			//返却型の取得
			try {
				CtMethod t_m = Tool.getCtMethod(null, cname, mname, atypes);
				returntype = t_m.getReturnType().getName();
				t_str += "$_=" + returntype + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
			} catch (NotFoundException e) {
				Tool.logIfDebug(e, "@MethodEditor#init"); //$NON-NLS-1$
			}
			
			//本体の表示
			t_str += "$0=" + cname + "\n"; //$NON-NLS-1$ //$NON-NLS-2$
			
			//引数型の表示
			for (int i = 0; i < atypes.length; i++) {
				if (i > 0) {
					t_str += "\n"; //$NON-NLS-1$
				}
				t_str += "$" + Integer.toString(i + 1) + "=" + atypes[i]; //$NON-NLS-1$ //$NON-NLS-2$
			}
			argTypes.setText(t_str);
			check();
		}
	}
	
	
	public FakeMethodRecord getFakeMethodRecord() {
		if (className.getText().length() > 0) {
			String a_type = ""; //$NON-NLS-1$

			//引数のみを抽出。
			final Pattern t_pat = Pattern.compile("\\$[1-9][0-9]*=([\\w\\.\\[\\]]+?)$"); //$NON-NLS-1$
			Matcher t_m = t_pat.matcher(argTypes.getText());

			String t_sep = ""; //$NON-NLS-1$
			while(t_m.find()) {
				String cname = t_m.group(1);
				a_type += t_sep + cname;
				
				if (t_sep.length() == 0) {
					t_sep = ","; //$NON-NLS-1$
				}
			}

			TestCommandRecord t_cmd = new TestCommandRecord(0);
			t_cmd.methodName = methodName.getText();//staticとかvarPosを落とす。
			
			FakeMethodRecord t_f = Tool._db().getMethodSource(className.getText(), t_cmd.getMethodName(), a_type.split(","), Tool.version); //$NON-NLS-1$
			if (t_f == null) {
				t_f = new FakeMethodRecord(2);
				t_f.className = className.getText();
				t_f.methodName = t_cmd.getMethodName();
				t_f.argTypes = a_type.split(","); //$NON-NLS-1$
			}
			t_f.source = textSource.getText();
			t_f.changed();
			
			//削除処理
			if (m_removing) {
				t_f.removed = true;
			}
			
			//_memVarsのセットから仮想メンバ変数を抽出
			CodeProcessor.preProcessMain(null, null, t_f.source);
			if (CodeProcessor._memVars != null) {
				for(Entry<String, String> entry : CodeProcessor._memVars.entrySet()) {
					if (entry.getValue() != null) {
						t_f.addField(entry.getValue(), entry.getKey());
					}
				}
			}
			return t_f;
		}
		return null;
	}
	
	protected void check() {
		String src = textSource.getText();
		src = CodeProcessor.preProcessMain(null, null, src);
		resultText.setText("");
		
		String t_str = CodeProcessor.checkSyntaxOfCode(textSource.getText());
		if (t_str != null) {
			resultText.setText(t_str);
		}

		//宣言されたメンバ変数の表示
		t_str = ""; //$NON-NLS-1$
		for(Entry<String, String> entry : CodeProcessor._memVars.entrySet()) {
			t_str += entry.getValue() + " " + entry.getKey() + ";\n"; //$NON-NLS-1$ //$NON-NLS-2$
		}
		fields.setText(t_str);
		resultText.setText(resultText.getText() + src);
	}
	
	//"."を受けて、
	//①現在の入力テキストを解析し、文字列とクラスのマップを作る。
	//②直前の文字列を解析し、クラスを特定する。必要な候補を表示する。$*.xxxまではフィールド、そこから先はpublicメソッド。
	//③選択された文字列をcaret位置に挿入する。
	private void popupCandidate(KeyEvent e) {
		String src = CodeProcessor.preProcess4Import(textSource.getText());
		CodeProcessor.preProcess4MemInfos(src); //$NON-NLS-1$
		HashMap<String, String> mems = CodeProcessor._memVars;
		
		//クラスマップの作成（仮想メンバ変数）
		HashMap<String, Class<?>> _cmap = new HashMap<String, Class<?>>();
		for(Entry<String, String> entry : mems.entrySet()) {
			String t_var = entry.getKey();
			String t_cname = entry.getValue();
			
			Class<?> t_c;
			try {
				if (t_cname != null) {
					t_c = Tool.forName(Tool.primitiveToFQN(t_cname));
					_cmap.put(t_var, t_c);
				}
			} catch (ClassNotFoundException e1) {
				Tool.alertMSG(null, t_cname + Messages.getString("MethodEditor.45")); //$NON-NLS-1$
			}
		}

		//$nのクラスマップを作る。
		String[] t_args = argTypes.getText().split("\n"); //$NON-NLS-1$
		for (int j = 0; j < t_args.length; j++) {
			String t_$ = t_args[j];
			String t_name = t_$.replaceAll("=.*", ""); //$NON-NLS-1$ //$NON-NLS-2$
			String c_name = t_$.replaceAll("\\$[_0-9]+=\\s*([\\w\\.\\$\\[\\]]+)[,]?", "$1"); //$NON-NLS-1$ //$NON-NLS-2$

			Class<?> t_c;
			try {
				t_c = Tool.forName(Tool.primitiveToFQN(c_name));
				_cmap.put(t_name, t_c);
			} catch (ClassNotFoundException e1) {
				Tool.logIfDebug(e1, "@MethodEditor#popupCandidate"); //$NON-NLS-1$
			}
		}
		
		Caret tc = textSource.getCaret();
		Point op = textSource.getLocationOnScreen();
		Point pt = tc.getMagicCaretPosition();
		Point pos = new Point(op.x + pt.x, op.y + pt.y);
		
		//popup表示フォーカス委譲（タイマーで）
		final CodingAssistMenu _popup = new CodingAssistMenu(this, getFakeMethodRecord(), textSource.getText(), textSource.getCaretPosition(), _cmap, pos, null);
		
		//候補がある場合のみポップアップする。
		if (_popup.countCandidate() > 0) {
			//このスレッドが終わるまではフォーカスされないのでタイマーでやる。（表示後でないとフォーカスされないため）
			Thread t_thread = new Thread() {
				@Override
				public void run() {
					try {
						sleep(1000);
						SwingUtilities.getWindowAncestor(_popup).toFront();
						_popup.requestFocusInWindow();
					} catch (InterruptedException e) {
						Tool.logIfDebug(e, "@MethodEditor#popupCandidate>run"); //$NON-NLS-1$
					}
				}
			};
			t_thread.start();
			
			_popup.setVisible(true);
			
			EventQueue.invokeLater(new Runnable() {
				@Override
				public void run() {
					insertAtCaret(_popup.m_answer, _popup.m_target, _popup.m_catches);
				}
			});
		}
	}
	

	//caretの位置にテキストを差し込む。
	public void insertAtCaret(String str, String tgt, String ctch) {
		if (str != null) {
			int pos = textSource.getCaretPosition();
			String str_h = textSource.getText();
			String str_tail = str_h.substring(pos);

			str_h = str_h.substring(0, pos);
			
			if (ctch != null) {
				//例外がある場合。
				str_h = str_h.replaceFirst(Pattern.quote(tgt + ".")+ "$", Matcher.quoteReplacement("try{\n" + tgt + ".")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
				str_h += ctch + str_tail; 
			} else {
				str_h += str + str_tail;
			}
			textSource.setText(str_h);
			textSource.setCaretPosition(pos + str.length());
		}
	}
	
	//入力支援ポップアップを開く。
	private void showPopup(MouseEvent e) {
		JPopupMenu t_pop = new JPopupMenu();
		t_pop.add(new JMenuItem(new MethodEditorAction("insert 'MOCK(...)'", this))); //$NON-NLS-1$
		t_pop.add(new JMenuItem(new MethodEditorAction("insert 'VGET(...)'", this))); //$NON-NLS-1$
		t_pop.show(e.getComponent(), e.getX(), e.getY());
	}
	
	class MethodEditorAction extends AbstractAction {
		MethodEditor m_editor;
		String m_cmd;

		MethodEditorAction(String menu_str, MethodEditor ed) {
			m_editor = ed;
			m_cmd = menu_str;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			if (m_cmd.matches("^.*insert.*OBJECT.*$")) { //$NON-NLS-1$
				m_editor.insertAtCaret("MOCK(\"" + returntype + "\")", null, null);
			} else if (m_cmd.matches("^.*insert.*VGET.*$")) { //$NON-NLS-1$
				String cname = className.getText().replaceFirst("^.*\\.([^\\.]+)$", "$1");
				m_editor.insertAtCaret("VGET(\"" + cname + "#"+ cname + "\")", null, null);
			}
		}
	}
}
