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

import java.awt.Component;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
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.sql.SQLException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.ListUI;

import com.ftinc.si.assist.run.Messages;
import com.ftinc.si.assist.test.ClassOutLine;
import com.ftinc.si.assist.test.Fson;
import com.ftinc.si.assist.test.JSONRecord;
import com.ftinc.si.assist.test.Tool;

public class JSONEditor extends JDialog {
	public static JDialog s_owner;
	
	private JTextField className;
	private JTextField cnameCurField;//現在選択中のフィールドのクラス名
	private ArrayList<String> m_targets;
	private ClassOutLine m_curOutline;
	private JComboBox<String> comboNames;
	private JComboBox<String> comboClasses;
	
	private String curFieldName;
	private HashMap<String, String> m_f2cname;//Field名からクラス名を引く。
	private HashMap<String, String> m_f2json;//元々DBにあったJSON文字列
	private HashMap<String, String> m_f2editing;//今回追加したJSON文字列
	private JEditorPane jsonPane;
	private DefaultListModel<String> m_lmodel = null;
	private DefaultComboBoxModel<String> m_cmodel;
	private String in_json;//外からJSONが与えられた場合。
	
	private boolean updatingComboNames = false;
	private ArrayList<JSONRecord> m_comboData = null;
	
	public JSONEditor(JDialog p, String cname, String subname, String source) {
		super(p, true); // modalにする。
		
		in_json = source;
		
		setTitle(Messages.getString("JSONEditor.0")); //$NON-NLS-1$
		setResizable(false);
		if (p != null) {
			setBounds(p.getX() + 100, p.getY() + 100, 800, 450);
		} else {
			setBounds(100, 100, 800, 450);
		}
		getContentPane().setLayout(null);
		
		m_targets = Tool.target_dirs;
		
		m_curOutline = Tool.getClassOutLine(cname);
		
		JLabel cnameLabel = new JLabel(Messages.getString("JSONEditor.1")); //$NON-NLS-1$
		cnameLabel.setBounds(12, 10, 77, 13);
		getContentPane().add(cnameLabel);
		
		m_cmodel = new DefaultComboBoxModel<String>();

		JButton findClass = new JButton(Messages.getString("JSONEditor.2")); //$NON-NLS-1$
		
		findClass.setBounds(428, 6, 100, 21);
		getContentPane().add(findClass);
		
		comboClasses = new JComboBox<String>(m_cmodel);
		comboClasses.setEditable(true);

		//comboboxでクラスを選んだら、そのFieldをリストに表示する。
		comboClasses.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (e.getStateChange() == ItemEvent.SELECTED) {
					updateCurFieldJson();

					//上下のクラスが選択されたら
					String t_str = (String)comboClasses.getSelectedItem();
					
					setFieldList(t_str);
				}
			}
		});
		
		comboClasses.setBounds(540, 7, 242, 19);
		getContentPane().add(comboClasses);
		
		setClassList2Combo(m_cmodel);
		
		JButton preview = new JButton(Messages.getString("JSONEditor.3")); //$NON-NLS-1$

		//実際にJSONでオブジェクトを作ってみる。結果をダイアログに表示する。
		preview.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (m_curOutline != null) {
					try {
						String t_str = getJson();
						Object t_obj = Tool.getObjectfromJSON(Tool.forName(className.getText()), t_str);
						Tool.alertMSG(s_owner, t_obj.toString());
					} catch (ClassNotFoundException e1) {
						Tool.alertMSG(s_owner, className.getText() + Messages.getString("JSONEditor.4"));  //$NON-NLS-1$
					}
				}
			}

		});
		preview.setBounds(69, 390, 91, 21);
		getContentPane().add(preview);
		
		JPanel panel = new JPanel();
		panel.setBounds(12, 33, 770, 347);
		getContentPane().add(panel);
		panel.setLayout(null);
		
		JLabel fieldsLabel = new JLabel(Messages.getString("JSONEditor.5")); //$NON-NLS-1$
		fieldsLabel.setBounds(12, 10, 114, 13);
		panel.add(fieldsLabel);
		
		JScrollPane scrollPane = new JScrollPane();
		scrollPane.setBounds(12, 36, 201, 301);
		panel.add(scrollPane);
		
		m_lmodel = new DefaultListModel<String>();
		final JList<String> fieldList = new JList<String>(m_lmodel);
		
		//リスト上で右クリックメニュー
		fieldList.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if(SwingUtilities.isRightMouseButton(e)){
					//右クリックならポップアップを開く。
					ListUI t_ui = fieldList.getUI();
					int j = t_ui.locationToIndex(fieldList, e.getPoint());
					if (j >= 0) {
						String label = (String)m_lmodel.get(j);
						goFieldPopUp(e, label.substring(3));
					} else {
						goFieldPopUp(e, null);
					}
				}
			}
		});
		
		scrollPane.setViewportView(fieldList);
		
		JScrollPane scrollPane_1 = new JScrollPane();
		scrollPane_1.setBounds(225, 36, 533, 301);
		panel.add(scrollPane_1);
		
		jsonPane = new JEditorPane();
		jsonPane.addMouseWheelListener(new MouseWheelListener() {
			public void mouseWheelMoved(MouseWheelEvent e) {
				//マウスホイールの操作により、フォントサイズを変える。
				Font t_font = Tool.resizeFont(e, jsonPane.getFont());
				if (t_font != null) {
					jsonPane.setFont(t_font);
				}
			}
		});
		jsonPane.setFont(new Font("MS UI Gothic", Font.PLAIN, 14));
		scrollPane_1.setViewportView(jsonPane);
		
		cnameCurField = new JTextField();
		cnameCurField.setEnabled(false);
		cnameCurField.setBounds(225, 7, 288, 19);
		panel.add(cnameCurField);
		cnameCurField.setColumns(10);
		
		final JButton templateButton = new JButton(Messages.getString("JSONEditor.6")); //$NON-NLS-1$
		templateButton.setBounds(530, 6, 123, 21);
		panel.add(templateButton);
		templateButton.setEnabled(false);
		
		//クラス名のみでJSONが作れる場合に、試みる。
		JButton btnTojson = new JButton(Messages.getString("JSONEditor.7")); //$NON-NLS-1$
		btnTojson.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//各フィールドのJSONの定型式を書く。
				String t_cname = cnameCurField.getText();
				
				String str_j = Tool.classToJSON(t_cname);
				if (str_j != null) {
					jsonPane.setText(str_j);
				}
			}
		});
		btnTojson.setBounds(667, 6, 91, 21);
		panel.add(btnTojson);
		
		JButton btnSave = new JButton(Messages.getString("JSONEditor.8")); //$NON-NLS-1$
		//そのまま保存する。
		btnSave.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (comboNames.getSelectedItem().equals(Messages.getString("JSONEditor.17"))) { //$NON-NLS-1$
					Tool.alertMSG(null, Messages.getString("JSONEditor.10")); //$NON-NLS-1$
				} else {
					saveJson((String)comboNames.getSelectedItem());//false はnewではないという意味。
				}
			}
		});
		btnSave.setBounds(346, 390, 91, 21);
		getContentPane().add(btnSave);
		if (in_json != null && in_json.length() > 0) {
			//JSONを編集するために開いたので、テンプレートとしての保存は不要。
			btnSave.setEnabled(false);
		}
		
		JButton btnClose = new JButton(Messages.getString("JSONEditor.13")); //$NON-NLS-1$
		btnClose.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				setVisible(false);
			}
		});
		btnClose.setBounds(449, 390, 91, 21);
		getContentPane().add(btnClose);
		
		className = new JTextField(cname);
		className.setBounds(86, 7, 202, 19);
		getContentPane().add(className);
		className.setColumns(10);
		
		comboNames = new JComboBox<String>();
		comboNames.setFont(new Font("MS UI Gothic", Font.PLAIN, 10)); //$NON-NLS-1$
		comboNames.setEditable(true);
		comboNames.setBounds(303, 7, 113, 19);
		getContentPane().add(comboNames);
		
		//Listが選択されたときに、右の領域を埋める。
		fieldList.addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent e) {
				//選択前JSONをHashMapに格納する。
				updateCurFieldJson();
				
				//右領域の置き換え。DBに格納済みのFieldか否かで若干処理が異なる。
				String t_fname = (String) fieldList.getSelectedValue();
				if (t_fname != null && t_fname.length() > 4) {
					curFieldName = t_fname.substring(3);//純粋なfield名を切り取る。
					
					cnameCurField.setText(m_f2cname.get(curFieldName));
					
					//表示の優先度は、編集中>DB>null
					if (m_f2editing.containsKey(curFieldName)) {//今回追加したField
						jsonPane.setText(m_f2editing.get(curFieldName));
					} else if (m_f2json.containsKey(curFieldName)) {
						jsonPane.setText(m_f2json.get(curFieldName));//元々DBにあったField
					} else {
						jsonPane.setText(""); //$NON-NLS-1$
					}
					templateButton.setEnabled(true);
				} else {
					jsonPane.setText(""); //$NON-NLS-1$
				}
			}
		});

		//JSONテンプレートからひな形を取得する。
		templateButton.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if (curFieldName != null && curFieldName.length() > 0) {
					JSONChooser chooser = new JSONChooser((JDialog)Tool.getParent((Component)e.getSource(), "JDialog"), Tool._db().getJSONRecWhere(m_f2cname.get(curFieldName), null, Tool.version)); //$NON-NLS-1$
					chooser.setVisible(true);
					if (chooser.is_ok) {
						jsonPane.setText(chooser.m_json);
						
						int i = fieldList.getSelectedIndex();
						String t_fname = (String) fieldList.getSelectedValue();
						String fName = t_fname.substring(3);//純粋なfield名を切り取る。
						if (t_fname.startsWith(Pattern.quote("(U)"))) { //$NON-NLS-1$
							m_lmodel.set(i, "(E)" + fName); //$NON-NLS-1$
						}
					}
				}
			}
		});

		
		//class fileからクラスを取得し、ComboBoxにスーパークラスリストを入れる。
		findClass.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				//クラス切り替えの前に、保存もしくは警告が必要。
				if (!jsonPane.getText().equals("")) { //$NON-NLS-1$
					int rc = JOptionPane.showConfirmDialog(null, Messages.getString("JSONEditor.19"), Messages.getString("JSONEditor.20"), JOptionPane.OK_CANCEL_OPTION); //$NON-NLS-1$ //$NON-NLS-2$
					switch (rc) {
					case JOptionPane.OK_OPTION:
						break;
					case JOptionPane.NO_OPTION:
					default:
						return;
					}
				}
				String cName = Tool.findClassName(Tool.getParent((Component)e.getSource(), "JDialog"), m_targets); //$NON-NLS-1$
				if (cName != null && cName.length() > 0) {
					m_curOutline = Tool.getClassOutLine(cName);
					
					className.setText(cName);
					
					setNameList2Combo(cName);
					setClassList2Combo(m_cmodel);
					clear();//画面上のFieldListとTextPaneをクリアする。

					//必要に応じcomboNamesの名前を差し替える。
					updateComboNames();
					
					initByDB();
					if (comboNames.getSelectedItem() != null) {
						setFieldList(className.getText());
					}
				}
			}
		});
		
		initByDB();
		
		if (comboNames.getSelectedItem() != null) {
			setFieldList(className.getText());
		}
		
		//!!イベント登録を最後にしないと、途中でイベントが発火する。
		//マウス操作で項目を変更した場合
		className.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				//enterでクラスを読み込むことを試みる。
				int t_key = e.getKeyCode();
				if (t_key == KeyEvent.VK_ENTER) {
					if (className.getText().length() > 0) {
						clear();
						initByDB();

						if (!updatingComboNames) {
							if (comboNames.getItemCount() == 0) {
								//副名候補が未設定なら設定する。
								setNameList2Combo(className.getText());
							} else {
								//副名候補が決まっているならFieldリストを作る。
								Object t_name = comboNames.getSelectedItem();
								if (t_name == null) {
									Tool.alertMSG(null, "Subname shoud be specified.");
								} else {
									setFieldList(className.getText());
								}
							}
						}
						
					} else {
						Tool.alertMSG(null, "Class Name not specified.");
					}
				}
			}
		});
		
		comboNames.addItemListener(new ItemListener() {
			public void itemStateChanged(ItemEvent e) {
				if (!updatingComboNames && e.getStateChange() == ItemEvent.SELECTED) {
					//副項目名の変更が起こると、クリアして初期化する。
					
					if (className.getText().length() > 0) {
						clear();
						
						//必要に応じcomboNamesの名前を差し替える。
						updateComboNames();
						
						initByDB();
						setFieldList(className.getText());
					} else {
						Tool.alertMSG(null, "Class Name not specified.");
					}
				}
			}
		});
	}

	//comboNamesであいまい検索した場合、comboNamesの内容を修正する。
	public void updateComboNames() {
		String name = (String)comboNames.getSelectedItem();
		if (name == null || name.length() == 0) {
			return;
		}

		updatingComboNames = true;
		
		//項目選択されたなら、クラス名が変更されている場合があるので、変更する。
		for (int j = 0; j < comboNames.getItemCount(); j++) {
			String t_name = comboNames.getItemAt(j);
			if (name.equals(t_name) && m_comboData != null) {
				for (int k =0; k < m_comboData.size(); k++) {
					if (m_comboData.get(k).name.equals(name)) {
						className.setText(m_comboData.get(k).className);
					}
				}
				updatingComboNames = false;
				return;
			}
		}
		//nameもしくはクラス名が手入力で変更された場合

		m_comboData = Tool._db().getJSONRecWhere(className.getText(), name, Tool.version);
		if (m_comboData.size() > 0) {
			comboNames.removeAllItems();
			for (int i = 0; i < m_comboData.size(); i++) {
				comboNames.addItem(m_comboData.get(i).name);
				if (i + 1 == m_comboData.size()) {
					comboNames.setSelectedItem(m_comboData.get(i).name);
					className.setText(m_comboData.get(i).className);
				}
			}
		}
		updatingComboNames = false;
		
	}

	//fieldlistとJSONPaneをクリアする。
	private void clear() {
		if (m_lmodel != null) {
			m_lmodel.removeAllElements();
			jsonPane.setText(""); //$NON-NLS-1$
		}
	}
	
	//現在のクラスが、基本型、配列、文字列、Listの場合、フィールドを持たないので、それを判定する。
	private boolean hasFields() {
		if (m_curOutline != null) {
			Class<?> t_c;
			try {
				t_c = Tool.forName(m_curOutline.getClassName());
			} catch (ClassNotFoundException e) {
				return false;
			}
			if (Tool.isPrimitive2(t_c) || java.util.List.class.isAssignableFrom(t_c)) {
				return false;
			}
		}
		return true;
	}
	
	//JSON情報をＤＢから取得し、設定済みfieldを取得する。
	private void initByDB() {
		m_f2cname = new HashMap<String, String>();
		m_f2json = new HashMap<String, String>();
		m_f2editing = new HashMap<String, String>();

		Object t_name = comboNames.getSelectedItem();
		
		if (className.getText().length() > 0) { //$NON-NLS-1$
			
			//与えられたJSONがあるか、サブ識別名が指定されていれば・・・
			if (in_json != null || comboNames.getSelectedItem() != null) {
				//与えられたJSONがコンストラクタ引数であろうがなかろうが、m_f2jsonに登録する。形式は同じなので。
				m_curOutline = Tool.getClassOutLine(className.getText());
				
				if (hasFields()) {
					HashMap<String, String> t_list = null;
					String src = ""; //$NON-NLS-1$
					String[] fnames = null;//フィールド名の配列
					
					if (in_json != null) {
						//JSONがJSONEditorのコンストラクタに与えられていれば、属性名をそこから取得する。
						src = in_json;

						//JSONの構文解析
						try {
							t_list = Fson.getFieldMapOfJson(src);
						} catch (ParseException e) {
							Tool.logIfDebug(e, " Illegal Json.");
							return;
						}
						
						ArrayList<String> flist = new ArrayList<String>();
						for(Entry<String, String> entry : m_f2json.entrySet()) {
							flist.add(entry.getKey());
						}
						fnames = flist.toArray(new String[flist.size()]);
					} else {
						//入力JSONがなしの場合、サブ識別名はあるのでJSONはDBから取れる。
						String name = (String)comboNames.getSelectedItem();
						JSONRecord jrec = Tool._db().getJSONRecord(className.getText(), name, Tool.version);

						//通常のJSONであれば編集可能
						if (jrec != null) {
							fnames = jrec.fields.split(",\n*"); //$NON-NLS-1$
							src = jrec.content;
						}

						//JSONの構文解析
						try {
							t_list = Fson.getFieldMapOfJson(src);
						} catch (ParseException e) {
							Tool.logIfDebug(e, " Illegal Json.");
							return;
						}
					}
					if (t_list != null) {
						m_f2json = t_list;
					} else {
						m_f2json.clear();
					}
					
					if (fnames != null) {
						if (className.getText().matches("^.*Map[<,>\\w]*$")) { //$NON-NLS-1$
							//Map系なら、Listに登録しておく。
							for (int i = 0; i < fnames.length; i++) {
								m_lmodel.addElement("(D)" + fnames[i]); //$NON-NLS-1$
							}
						}
					}
				} else {
					cnameCurField.setText(className.getText());
					String src= "";
					if (in_json != null) {
						//JSONがJSONEditorのコンストラクタに与えられていれば、属性名をそこから取得する。
						src = in_json;
					} else {
						//入力JSONがなしの場合、サブ識別名はあるのでJSONはDBから取れる。
						String name = (String)comboNames.getSelectedItem();
						JSONRecord jrec = Tool._db().getJSONRecord(className.getText(), name, Tool.version);
						src = jrec.content;
					}
					
					jsonPane.setText(src);
				}
				
			} else if (t_name != null) {
				//入力JSONはない
			}
		}
	}
	
	//指定されたクラス名に該当するJSONデータの配列を取得し、comboNames（サブ識別名リスト）に入れる。
	private void setNameList2Combo(String cname) {
		if (cname != null && cname.length() > 0) {
			m_curOutline = Tool.getClassOutLine(cname);

			ArrayList<JSONRecord> jdata = Tool._db().getJSONRecWhere(cname, null, Tool.version);
			comboNames.removeAllItems();

			for (int i = 0; i < jdata.size(); i++) {
				comboNames.addItem(jdata.get(i).name);
			}
		
			if (m_curOutline != null) {
				if (!Tool.hasDefaultConstructor(m_curOutline.getCtClass(), false)) {
					//デフォルトコンストラクタがない場合。コンストラクタ引数用JSONを作れるようにする。
					comboNames.addItem("CONSTRUCTOR"); //$NON-NLS-1$
				}
			}

			if (comboNames.getItemCount() == 0) {
				Tool.alertMSG(null, Messages.getString("JSONEditor.14")); //$NON-NLS-1$
			}
		}
	}


	//クラス名からFieldのリストをセットする。
	private void setFieldList(String t_str) {
		if (hasFields()) {
			clear();
			
			ClassOutLine co = Tool.getClassOutLine(t_str);
			
			if (co != null && m_lmodel != null) {
				m_lmodel.removeAllElements();

				if (!co.getClassName().matches("^.*Map[<,>\\w]*$")) {//HashMapでないなら、クラスからFieldを取得する。 //$NON-NLS-1$
					String t_comboName = ""; //$NON-NLS-1$
					if (comboNames.getSelectedItem() != null) {
						t_comboName = comboNames.getSelectedItem().toString();
					}
					
					HashMap<String, String> t_flds = null;//fieldname, classnameの組(static,finalは除外されている)

					if ("CONSTRUCTOR".equals(t_comboName)) { //$NON-NLS-1$
						if (Tool.hasDefaultConstructor(co.getCtClass(), false)) {
							//不要
							Tool.alertMSG(null, Messages.getString("JSONEditor.15")); //$NON-NLS-1$
							return;
						}
						ConstructorSelector t_cs = new ConstructorSelector(this, co);
						t_cs.setVisible(true);
						if (!t_cs.is_OK || t_cs.getParameters(false).size() == 0) {
							Tool.alertMSG(null, Messages.getString("JSONEditor.16")); //$NON-NLS-1$
							return;
						}
						t_flds = t_cs.getParameters(false);
					} else {
						//上記以外の場合。
						t_flds = co.getDeclaredFields(null);
						
						//デフォルトコンストラクタがない場合、最初のコンストラクタを要素に入れる。
						if (!Tool.hasDefaultConstructor(co.getCtClass(), false)) {
							ConstructorSelector t_cs = new ConstructorSelector(this, co);
							t_flds.putAll(t_cs.getParameters(true));
						}
					}
					
					String t_fname;
					for(Entry<String, String> entry : t_flds.entrySet()) {
						t_fname = entry.getKey();

						String t_item = null;
						
						//Listに入れるときにJSON設定済みかどうかで表示を変える。
						if (m_f2editing.containsKey(t_fname)) {
							t_item = "(E)" + t_fname;//DBには未格納だが編集中の値はある。 //$NON-NLS-1$
						} else if (m_f2json.containsKey(t_fname)) {
							t_item = "(D)" + t_fname;//DBに格納済み。 //$NON-NLS-1$
						} else {
							t_item = "(U)" + t_fname;	//未設定 //$NON-NLS-1$
						}
						if (t_item != null) {
							m_lmodel.addElement(t_item);
						}
						
						//フィールドのクラス情報を格納する。
						if (!m_f2cname.containsKey(t_fname)) {
							m_f2cname.put(t_fname, entry.getValue());
						}
					}
				} else {//HashMapならm_f2jsonのみに入っている。
					for(Entry<String, String> entry : m_f2json.entrySet()) {
						m_lmodel.addElement("(D)" + entry.getKey()); //$NON-NLS-1$
					}
				}
			}
			curFieldName = "";//前の選択情報を消去する。 //$NON-NLS-1$		}
		}
	}
	

	//スーパークラスのリストを作成しcomboBoxにセットする。
	private void setClassList2Combo(DefaultComboBoxModel<String> model) {
		if (m_curOutline != null && hasFields()) {
			model.removeAllElements();
			String cname = m_curOutline.getClassName();
			model.addElement(cname);
			ClassOutLine s_class = m_curOutline;
			while ((s_class = s_class.getSuper()) != null) {
				if (!s_class.getClassName().equals("java.lang.Object")) {	//java.lang.Objectは不要 //$NON-NLS-1$
					model.addElement(s_class.getClassName());
				}
			}
		}
	}
	
	//JSONRecordのFields情報（リスト）を形成する。
	private String getFields() {
		String t_res = ""; //$NON-NLS-1$
		boolean _1st = true;
		for(Entry<String, String> entry : m_f2editing.entrySet()) {
			if (!_1st) {
				t_res += ",\n"; //$NON-NLS-1$
			} else {
				_1st = false;
			}
			t_res += entry.getKey();
		}
		for(Entry<String, String> entry : m_f2json.entrySet()) {
			if (!_1st) {
				t_res += ",\n"; //$NON-NLS-1$
			} else {
				_1st = false;
			}
			t_res += entry.getKey();
		}
		return t_res;
	}
	
	//JSON形式で文字列を形成する。
	public String getJson() {
		if (hasFields()) {
			String t_str = "{\n"; //$NON-NLS-1$
			boolean _1st = true;
			
			ArrayList<String> t_flds = new ArrayList<String>();
			
			for(Entry<String, String> entry : m_f2editing.entrySet()) {
				//新規追加
				if (!_1st) {
					t_str += ",\n"; //$NON-NLS-1$
				} else {
					_1st = false;
				}
				t_str += entry.getKey() + ":" + entry.getValue(); //$NON-NLS-1$
				t_flds.add(entry.getKey());
			}

			for(Entry<String, String> entry : m_f2json.entrySet()) {
				//元々DBにあったもの
				if (!_1st) {
					t_str += ",\n"; //$NON-NLS-1$
				} else {
					_1st = false;
				}
				t_str += entry.getKey() + ":" + entry.getValue(); //$NON-NLS-1$
			}

			return t_str + "}"; //$NON-NLS-1$
		}
		return jsonPane.getText();
	}
	
	//fieldの編集状態を更新する。
	private void updateCurFieldJson() {
		if (curFieldName != null) {
			if (m_f2json.containsKey(curFieldName)) {
				//元々DBにあったものはm_f2jasonに格納する。
				m_f2json.put(curFieldName, jsonPane.getText());
			} else {	//その他はDBにないので編集用Mapで管理する。
				if (!jsonPane.getText().equals("")) { //$NON-NLS-1$
					m_f2editing.put(curFieldName, jsonPane.getText());
					
					//UをEに変える。
					for (int i = 0; i < m_lmodel.size(); i++) {
						if (m_lmodel.getElementAt(i).equals("(U)" + curFieldName)) { //$NON-NLS-1$
							m_lmodel.setElementAt("(E)" + curFieldName, i); //$NON-NLS-1$
							break;
						}
					}
				} else if (m_f2editing.get(curFieldName) != null) {
					//nullなら削除する。
					m_f2editing.remove(curFieldName);
				}
			}
		}

	}
	
	//ポップアップメニューを合成する。
	private void goFieldPopUp(MouseEvent e, String label) {
		JPopupMenu t_pop = new JPopupMenu();

		if (m_curOutline.getClassName().matches("^.*Map[<,>\\w]*$")) { //$NON-NLS-1$
			//Map系なので有効にする。
			if (label != null) {
				t_pop.add(new JMenuItem(new JSONAction(Messages.getString("JSONEditor.56"), this, label))); //$NON-NLS-1$
				t_pop.add(new JMenuItem(new JSONAction(Messages.getString("JSONEditor.57"), this, label))); //$NON-NLS-1$
			}
			t_pop.add(new JMenuItem(new JSONAction(Messages.getString("JSONEditor.58"), this, label))); //$NON-NLS-1$
			
			t_pop.show(e.getComponent(), e.getX(), e.getY());
		}
	}
	
	//popupのコマンド文字列を受けて、JSONEditorのメソッドに中継する。
	//中継だけなので、アクションはこのクラスのみ。
	class JSONAction extends AbstractAction {
		String m_actname;
		JSONEditor m_master;
		String m_label;

		JSONAction(String aname, JSONEditor master, String cname) {
			putValue(Action.NAME, aname);
			m_actname = aname;
			m_master = master;
			m_label = cname;
		}

		@Override
		public void actionPerformed(ActionEvent e) {
			if  (m_actname.equals(Messages.getString("JSONEditor.59"))) { //$NON-NLS-1$
				m_master.editField(m_label);
			} else if (m_actname.equals(Messages.getString("JSONEditor.60"))) { //$NON-NLS-1$
				m_master.addField();
			} else if (m_actname.equals(Messages.getString("JSONEditor.61"))) { //$NON-NLS-1$
				m_master.removeField(m_label);
			}
		}
	}

	//どうせJSONなのだから、fieldの名前だけでいい。
	private void addField() {
		String t_val = JOptionPane.showInputDialog(this, Messages.getString("JSONEditor.62")); //$NON-NLS-1$
		if (t_val != null){
			m_lmodel.addElement("(U)" + t_val); //$NON-NLS-1$
		}
	}

	//指定のfieldをリストから外す
	private void removeField(String label) {
		for (int i = 0; i < m_lmodel.size(); i++) {
			if (m_lmodel.getElementAt(i).equals("(U)" + label)) { //$NON-NLS-1$
				m_lmodel.removeElementAt(i);
				break;
			}
		}
	}

	//field名を入力する文字で置き換える。（宣言されたフィールドでは不可）
	private void editField(String label) {
		String t_val = JOptionPane.showInputDialog(this, Messages.getString("JSONEditor.65")); //$NON-NLS-1$
		if (t_val != null){
			for (int i = 0; i < m_lmodel.size(); i++) {
				if (m_lmodel.getElementAt(i).equals("(U)" + label)) { //$NON-NLS-1$
					m_lmodel.setElementAt(t_val, i);
					break;
				}
			}
		}
	}
	
	//save用共通処理
	private void saveJson(String name) {
		//編集中JSONをHashMapに格納する。
		updateCurFieldJson();
		
		//起動時は新規作成でも二度保存ボタンを押すことがある。
		JSONRecord t_j = Tool._db().getJSONRecord(className.getText(), name, Tool.version);
		if (t_j == null) {
			//insert
			t_j = new JSONRecord(2);
			t_j.name = name;
			t_j.className = className.getText();
			t_j.content = getJson();
			t_j.fields = getFields();
		} else {
			//update
			t_j.content = getJson();
			t_j.fields = getFields();
		}
		if (t_j != null) {
			try {
				Tool._db().updateRecordBySQL(t_j.getUpdateSQL(), true);
				//ここでDB更新
			} catch (SQLException e) {
				Tool.alertMSG(null, Messages.getString("JSONEditor.9") + e.getMessage()); //$NON-NLS-1$
			}
		}
	}
}
