import java.io.*;
import java.util.regex.*;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

class HedFile2 extends HedFile {
    private JTextArea mTextArea;

    public HedFile2(File dataDir, File decodedDir) {
	super(dataDir, decodedDir);
	mTextArea = null;
    }

    public HedFile2(File dataDir, File decodedDir, File hedFile) throws IOException {
	super(dataDir, decodedDir, hedFile);
    }

    public void setTextArea(JTextArea text) {
	mTextArea = text;
    }

    public String getStatus(HedMember member) {
	File decoded = new File(mDecodedDir, member.mFile.getPath());
	if(!decoded.isFile())
	    return "not extracted";
	if(member.mMemberSize == decoded.length()
	   && member.mUpdatedTime == decoded.lastModified() / 1000)
	    return "not modified";
	return "modified";
    }

    @Override
    public String toString() {
	return mFile.getPath();
    }

    protected void extractedMember(HedMember member) {
	if(mTextArea != null) {
	    mTextArea.append("\t\t展開: " + member.getPath() + "\n");
	    mTextArea.setCaretPosition(mTextArea.getDocument().getLength());
	}
    }

    protected void storedMember(HedMember member) {
	if(mTextArea != null) {
	    mTextArea.append("\t\t格納: " + member.getPath() + "\n");
	    mTextArea.setCaretPosition(mTextArea.getDocument().getLength());
	}
    }

    protected void replaced(File src, File dst) {
	if(mTextArea != null) {
	    mTextArea.append("\t置換: "
			     + src.getPath() + "→" + dst.getPath() + "\n");
	    mTextArea.setCaretPosition(mTextArea.getDocument().getLength());
	}
    }
}

public class MainFrame3 extends javax.swing.JFrame {

    private HedEnviron mEnv;

    static private Pattern HED_FILE_PATTERN = Pattern.compile(".*\\.hed$");
    static private RegexFileFinder HED_FILE_FINDER =
	new RegexFileFinder(HED_FILE_PATTERN);
    private static final String BACKUP_EXT = FileUtils.BACKUP_EXT;

    /** Creates new form MainFrame3 */
    public MainFrame3(HedEnviron env) {
	mEnv = env;

	initComponents();

	// hedファイルのリストを設定
	hedList.setModel(new DefaultListModel());
	createHedFileList();

	// hedファイルのJListの選択状態が変わった時、
	// メンバ表示テーブルを更新するようにリスナを登録
	hedList.addListSelectionListener(new ListSelectionListener() {
	    public void valueChanged(ListSelectionEvent e) {
		if(e.getSource() != hedList) return;
		DefaultListModel model = (DefaultListModel)hedList.getModel();
		int index = hedList.getSelectedIndex();
		if(index < 0 || index >= model.size())
		    return; // 選択行が削除された場合
		HedFile2 hedfile = (HedFile2)model.get(index);
		hedMemberTable1.displayHedFile(hedfile);
	    }
	});

	jList1.setModel(new DefaultListModel());
	jList2.setModel(new DefaultListModel());
    }

    private void createHedFileList() {
	File[] files = HED_FILE_FINDER.find(new File(mEnv.dataDir)).getArray();
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	model.clear();
	for(int i = 0; i < files.length; i++) {
	    try {
		HedFile2 hed = new HedFile2(new File(mEnv.dataDir),
					    new File(mEnv.decodedDir),
					    files[i]);
		hed.setTextArea(jTextArea1);
		model.add(i, hed);
	    } catch(IOException e) {
	    }
	}
    }

    private void updateHedFileList(int index) {
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	HedFile2 orig = (HedFile2)model.get(index);
	try {
	    HedFile2 hed = new HedFile2(orig.mDataDir, orig.mDecodedDir,
					orig.mFile);
	    hed.setTextArea(jTextArea1);
	    model.set(index, hed);
	} catch(IOException e) {
	}
    }

    private void output(String s) {
	jTextArea1.append(s);
	jTextArea1.setCaretPosition(jTextArea1.getDocument().getLength());
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        jSplitPane3 = new javax.swing.JSplitPane();
        textAreaScrollPane = new javax.swing.JScrollPane();
        jTextArea1 = new javax.swing.JTextArea();
        jTabbedPane2 = new javax.swing.JTabbedPane();
        jPanel3 = new javax.swing.JPanel();
        jSplitPane1 = new javax.swing.JSplitPane();
        jPanel1 = new javax.swing.JPanel();
        extractMemberButton = new javax.swing.JButton();
        forcedExtractChkBtn = new javax.swing.JCheckBox();
        forcedRebuildChkBtn = new javax.swing.JCheckBox();
        rebuildDatButton = new javax.swing.JButton();
        purgeDatButton = new javax.swing.JButton();
        restoreDatButton = new javax.swing.JButton();
        jButton7 = new javax.swing.JButton();
        jButton8 = new javax.swing.JButton();
        restoreMemberButton = new javax.swing.JButton();
        purgeMemberButton = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        jSplitPane2 = new javax.swing.JSplitPane();
        hedListScrollPane = new javax.swing.JScrollPane();
        hedList = new javax.swing.JList();
        hedMemberTable1 = new MemberInfoPanel();
        jPanel2 = new javax.swing.JPanel();
        jRadioButton1 = new javax.swing.JRadioButton();
        jRadioButton2 = new javax.swing.JRadioButton();
        jRadioButton3 = new javax.swing.JRadioButton();
        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jScrollPane2 = new javax.swing.JScrollPane();
        jList2 = new javax.swing.JList();
        replaceButton = new javax.swing.JButton();
        jLabel4 = new javax.swing.JLabel();
        jLabel5 = new javax.swing.JLabel();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setMinimumSize(new java.awt.Dimension(1000, 600));

        jSplitPane3.setDividerLocation(450);
        jSplitPane3.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        jTextArea1.setColumns(80);
        jTextArea1.setTabSize(2);
        textAreaScrollPane.setViewportView(jTextArea1);

        jSplitPane3.setRightComponent(textAreaScrollPane);

        jSplitPane1.setDividerLocation(300);
        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        extractMemberButton.setText("メンバファイルを展開");
        extractMemberButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                extractMemberButtonActionPerformed(evt);
            }
        });

        forcedExtractChkBtn.setText("強制");

        forcedRebuildChkBtn.setText("強制");

        rebuildDatButton.setText("datファイルを再構築");
        rebuildDatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                rebuildDatButtonActionPerformed(evt);
            }
        });

        purgeDatButton.setText("削除");
        purgeDatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                purgeDatButtonActionPerformed(evt);
            }
        });

        restoreDatButton.setText("復元");
        restoreDatButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                restoreDatButtonActionPerformed(evt);
            }
        });

        jButton7.setText("除外");
        jButton7.setEnabled(false);

        jButton8.setText("追加");
        jButton8.setEnabled(false);

        restoreMemberButton.setText("復元");
        restoreMemberButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                restoreMemberButtonActionPerformed(evt);
            }
        });

        purgeMemberButton.setText("削除");
        purgeMemberButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                purgeMemberButtonActionPerformed(evt);
            }
        });

        jLabel1.setText("メンバファイルのバックアップ");

        jLabel2.setText("datファイルのバックアップ");

        jLabel3.setText("メンバファイル");
        jLabel3.setEnabled(false);

        javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
        jPanel1.setLayout(jPanel1Layout);
        jPanel1Layout.setHorizontalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(extractMemberButton, javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(rebuildDatButton, javax.swing.GroupLayout.Alignment.LEADING))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(forcedRebuildChkBtn)
                    .addComponent(forcedExtractChkBtn))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel3)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(jButton8)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(jButton7))
                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup()
                        .addComponent(restoreDatButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                        .addComponent(purgeDatButton))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addComponent(restoreMemberButton)
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                        .addComponent(purgeMemberButton)))
                .addContainerGap(142, Short.MAX_VALUE))
        );
        jPanel1Layout.setVerticalGroup(
            jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel1Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(extractMemberButton)
                            .addComponent(forcedExtractChkBtn)
                            .addComponent(jLabel1))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(rebuildDatButton)
                            .addComponent(forcedRebuildChkBtn)
                            .addComponent(jLabel2)))
                    .addGroup(jPanel1Layout.createSequentialGroup()
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(purgeMemberButton)
                            .addComponent(restoreMemberButton))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(purgeDatButton)
                            .addComponent(restoreDatButton))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jButton7)
                            .addComponent(jButton8)
                            .addComponent(jLabel3))))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        jSplitPane1.setRightComponent(jPanel1);

        jSplitPane2.setDividerLocation(200);

        hedList.setFont(hedList.getFont().deriveFont(hedList.getFont().getStyle() & ~java.awt.Font.BOLD));
        hedListScrollPane.setViewportView(hedList);

        jSplitPane2.setLeftComponent(hedListScrollPane);
        jSplitPane2.setRightComponent(hedMemberTable1);

        jSplitPane1.setTopComponent(jSplitPane2);

        javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3);
        jPanel3.setLayout(jPanel3Layout);
        jPanel3Layout.setHorizontalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 707, Short.MAX_VALUE)
        );
        jPanel3Layout.setVerticalGroup(
            jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jSplitPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 421, Short.MAX_VALUE)
        );

        jTabbedPane2.addTab("展開・構築", jPanel3);

        buttonGroup1.add(jRadioButton1);
        jRadioButton1.setText("アイテム");
        jRadioButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jRadioButton1ActionPerformed(evt);
            }
        });

        buttonGroup1.add(jRadioButton2);
        jRadioButton2.setText("キャラクタ");
        jRadioButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jRadioButton2ActionPerformed(evt);
            }
        });

        buttonGroup1.add(jRadioButton3);
        jRadioButton3.setText("フィールド");
        jRadioButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jRadioButton3ActionPerformed(evt);
            }
        });

        jList1.setFont(jList1.getFont().deriveFont(jList1.getFont().getStyle() & ~java.awt.Font.BOLD));
        jList1.setModel(new DefaultListModel());
        jList1.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane1.setViewportView(jList1);

        jList2.setFont(jList2.getFont().deriveFont(jList2.getFont().getStyle() & ~java.awt.Font.BOLD));
        jList2.setModel(new DefaultListModel());
        jList2.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jScrollPane2.setViewportView(jList2);

        replaceButton.setText("置換");
        replaceButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                replaceButtonActionPerformed(evt);
            }
        });

        jLabel4.setText("置換されるID");

        jLabel5.setText("参照されるID");

        javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
        jPanel2.setLayout(jPanel2Layout);
        jPanel2Layout.setHorizontalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addComponent(jRadioButton1)
                        .addGap(18, 18, 18)
                        .addComponent(jRadioButton2)
                        .addGap(18, 18, 18)
                        .addComponent(jRadioButton3)
                        .addGap(65, 65, 65)
                        .addComponent(replaceButton))
                    .addGroup(jPanel2Layout.createSequentialGroup()
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 244, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel4))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel5)
                            .addComponent(jScrollPane2, javax.swing.GroupLayout.PREFERRED_SIZE, 280, javax.swing.GroupLayout.PREFERRED_SIZE))))
                .addGap(153, 153, 153))
        );
        jPanel2Layout.setVerticalGroup(
            jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(jPanel2Layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jRadioButton1)
                    .addComponent(jRadioButton2)
                    .addComponent(jRadioButton3)
                    .addComponent(replaceButton))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel4)
                    .addComponent(jLabel5))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 343, Short.MAX_VALUE)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 343, Short.MAX_VALUE))
                .addContainerGap())
        );

        jTabbedPane2.addTab("置換", jPanel2);

        jSplitPane3.setLeftComponent(jTabbedPane2);

        jMenu1.setText("ファイル");

        jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK));
        jMenuItem1.setText("終了");
        jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem1ActionPerformed(evt);
            }
        });
        jMenu1.add(jMenuItem1);

        jMenuBar1.add(jMenu1);

        setJMenuBar(jMenuBar1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jSplitPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 714, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jSplitPane3, javax.swing.GroupLayout.DEFAULT_SIZE, 551, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void extractMemberButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractMemberButtonActionPerformed
	final DefaultListModel model = (DefaultListModel)hedList.getModel();
	final boolean forced = forcedExtractChkBtn.isSelected();

	output("展開処理の開始\n");
	(new SwingWorker<Object, String>() {

	    public Object doInBackground() {
		for(int i = 0; i < model.size(); i++) {
		    if(!hedList.isSelectedIndex(i))
			continue;

		    HedFile2 hed = (HedFile2)model.get(i);
		    output("\t展開開始: " + hed + "\n");
		    try {
			hed.extractAll(forced);
			output("\t展開成功: " + hed + "\n");
		    } catch (Exception e) {
			output("\t展開失敗: " + hed + "\n");
			output(getDesc(e));
		    }
		}
		return null;
	    }

	    protected void done() {
		output("展開処理の終了\n");
		for(int i = 0; i < model.size(); i++)
		    if(hedList.isSelectedIndex(i))
			updateHedFileList(i);
	    }
	}).execute();
    }//GEN-LAST:event_extractMemberButtonActionPerformed

    private void restoreMemberButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restoreMemberButtonActionPerformed
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	for(int i = 0; i < model.size(); i++) {
	    if(!hedList.isSelectedIndex(i))
		continue;
	    restoreMembers((HedFile2)model.get(i));
	}
    }//GEN-LAST:event_restoreMemberButtonActionPerformed

    private void restoreMembers(HedFile2 hed) {
	output("メンバのバックアップを復元: " + hed + "\n");
	for(int m = 0; m < hed.mMembers.size(); m++) {
	    HedMember member = hed.mMembers.get(m);
	    File orig = new File(mEnv.decodedDir, member.mFile.getPath());
	    File backup = new File(mEnv.decodedDir,
				   member.mFile.getPath() + BACKUP_EXT);
	    if(backup.isFile()) {
		orig.delete();
		backup.renameTo(orig);
		output("\t" + orig.getPath() + "\n");
	    }
	}
    }

    private void purgeMemberButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_purgeMemberButtonActionPerformed
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	for(int i = 0; i < model.size(); i++) {
	    if(!hedList.isSelectedIndex(i))
		continue;
	    purgeMembers((HedFile2)model.get(i));
	}
    }//GEN-LAST:event_purgeMemberButtonActionPerformed

    private void purgeMembers(HedFile2 hed) {
	output("メンバのバックアップを削除: " + hed + "\n");
	for(int m = 0; m < hed.mMembers.size(); m++) {
	    HedMember member = hed.mMembers.get(m);
	    File backup = new File(mEnv.decodedDir,
				   member.mFile.getPath() + BACKUP_EXT);
	    if(backup.isFile()) {
		backup.delete();
		output("\t" + backup.getPath() + "\n");
	    }
	}
    }

    private void rebuildDatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rebuildDatButtonActionPerformed
	final DefaultListModel model = (DefaultListModel)hedList.getModel();
	final boolean forced = forcedRebuildChkBtn.isSelected();

	output("再構築処理の開始\n");
	(new SwingWorker<Object, String>() {

	    public Object doInBackground() {
		for(int i = 0; i < model.size(); i++) {
		    if(!hedList.isSelectedIndex(i))
			continue;

		    HedFile2 hed = (HedFile2)model.get(i);
		    output("\t再構築開始: " + hed + "\n");
		    try {
			hed.storeAll(forced);
			output("\t再構築成功: " + hed + "\n");
		    } catch(Exception e) {
			output("\t再構築失敗: " + hed + "\n");
			output(getDesc(e));
		    }
		}
		return null;
	    }

	    protected void done() {
		output("再構築処理の終了\n");
		for(int i = 0; i < model.size(); i++)
		    if(hedList.isSelectedIndex(i))
			updateHedFileList(i);
	    }
	}).execute();
    }//GEN-LAST:event_rebuildDatButtonActionPerformed

    private void restoreDatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restoreDatButtonActionPerformed
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	for(int i = 0; i < model.size(); i++) {
	    if(!hedList.isSelectedIndex(i))
		continue;
	    restoreHedDat((HedFile2)model.get(i));
	}
    }//GEN-LAST:event_restoreDatButtonActionPerformed

    private void restoreHedDat(HedFile2 hed) {
	output("hed/datのバックアップを復元: " + hed + "\n");
	File backup = new File(hed.mFile.getPath() + BACKUP_EXT);
	if(backup.isFile()) {
	    hed.mFile.delete();
	    backup.renameTo(hed.mFile);
	    output("\t" + hed.mFile.getPath() + "\n");
	}

	// datファイルのバックアップを復元
	for(int m = 0; m <= hed.mMaxDatIndex; m++) {
	    File dat = new File(hed.mDataDir, String.format(hed.mTemplate, m));
	    backup = new File(dat.getPath() + BACKUP_EXT);
	    if(backup.isFile()) {
		dat.delete();
		backup.renameTo(dat);
		output("\t" + dat.getPath() + "\n");
	    }
	}
    }

    private void purgeDatButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_purgeDatButtonActionPerformed
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	for(int i = 0; i < model.size(); i++) {
	    if(!hedList.isSelectedIndex(i))
		continue;
	    purgeHedDat((HedFile2)model.get(i));
	}
    }//GEN-LAST:event_purgeDatButtonActionPerformed

    private void purgeHedDat(HedFile2 hed) {
	output("hedファイルのバックアップを削除: " + hed + "\n");
	File backup = new File(hed.mFile.getPath() + BACKUP_EXT);
	if(backup.isFile()) {
	    backup.delete();
	    output("\t" + backup.getPath() + "\n");
	}

	// datファイルのバックアップを削除
	for(int m = 0; m <= hed.mMaxDatIndex; m++) {
	    File dat = new File(hed.mDataDir, String.format(hed.mTemplate, m));
	    backup = new File(dat.getPath() + BACKUP_EXT);
	    if(backup.isFile()) {
		backup.delete();
		output("バックアップ削除: " + dat.getPath() + "\n");
	    }
	}
    }

    private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jMenuItem1ActionPerformed
	System.exit(0);
    }//GEN-LAST:event_jMenuItem1ActionPerformed

    private void replaceButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_replaceButtonActionPerformed
	if(jRadioButton1.isSelected()) {
	    HedFile2 hed = findHedFileByName("data/item.hed");
	    if(hed == null)
		throw new RuntimeException("cannot find item.hed");
	    String virtId = ItemList.array[jList1.getSelectedIndex()][0];
	    String realId = ItemList.array[jList2.getSelectedIndex()][0];
	    output("アイテム置換: " + virtId + " ← " + realId + "\n");
	    try {
		hed.replaceItem(virtId, realId);
	    } catch(IOException e) {
		output("アイテム置換失敗\n");
	    }
	} else if(jRadioButton2.isSelected()) {
	    HedFile2 hed = findHedFileByName("data/chara/1.hed");
	    if(hed == null)
		throw new RuntimeException("cannot find chara/1.hed");
	    String virtId = CharaList.array[jList1.getSelectedIndex()][0];
	    String realId = CharaList.array[jList2.getSelectedIndex()][0];
	    output("キャラクタ置換: " + virtId + " ← " + realId + "\n");
	    try {
		hed.replaceChara(virtId, realId);
	    } catch(IOException e) {
		output("アイテム置換失敗\n");
	    }
	} else if(jRadioButton3.isSelected()) {
	    HedFile2 hed = findHedFileByName("data/world/field.hed");
	    if(hed == null)
		throw new RuntimeException("cannot find world/field.hed");
	    String virtId = "R01";
	    String realId = FieldList.array[jList2.getSelectedIndex()][0];
	    output("フィールド置換: " + virtId + " ← " + realId + "\n");
	    try {
		hed.replaceField(virtId, realId);
	    } catch(IOException e) {
		output("フィールド置換失敗\n");
	    }
	}
    }//GEN-LAST:event_replaceButtonActionPerformed

    private HedFile2 findHedFileByName(String path) {
	DefaultListModel model = (DefaultListModel)hedList.getModel();
	File file = new File(path);
	for(int i = 0; i < model.size(); i++) {
	    HedFile2 hed = (HedFile2)model.get(i);
	    if(hed.mFile.equals(file))
		return hed;
	}
	return null;
    }

    private void jRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRadioButton1ActionPerformed
	setReplaceList(ItemList.array);
    }//GEN-LAST:event_jRadioButton1ActionPerformed

    private void jRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRadioButton2ActionPerformed
	setReplaceList(CharaList.array);
    }//GEN-LAST:event_jRadioButton2ActionPerformed

    private void jRadioButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jRadioButton3ActionPerformed
	DefaultListModel model1 = (DefaultListModel)jList1.getModel();
	DefaultListModel model2 = (DefaultListModel)jList2.getModel();
	model1.clear();
	model2.clear();
	model1.addElement("R01: マイルーム");
	String[][] array = FieldList.array;
	for(int i = 0; i < array.length; i++)
	    model2.addElement(array[i][0] + ": " + array[i][1]);
    }//GEN-LAST:event_jRadioButton3ActionPerformed

    private void setReplaceList(String[][] array) {
	DefaultListModel model1 = (DefaultListModel)jList1.getModel();
	DefaultListModel model2 = (DefaultListModel)jList2.getModel();
	model1.clear();
	model2.clear();
	for(int i = 0; i < array.length; i++) {
	    model1.addElement(array[i][0] + ": " + array[i][1]);
	    model2.addElement(array[i][0] + ": " + array[i][1]);
	}
    }

    /** 発生した例外の情報を文字列化する */
    private static String getDesc(Throwable e) {
	Throwable th = e;
	StringBuffer sb = new StringBuffer();

	while(th != null) {
	    if(th != e)
		sb.append("\nCaused by: "); // 2番目以降
	    sb.append(th.getClass().getName());

	    String s = th.getMessage();
	    if(s != null)
		sb.append(": " + s);

	    StackTraceElement[] ste = th.getStackTrace();
	    for(int i = 0; i < ste.length; i++) {
		s = ste[i].toString();
		sb.append("\n	     at ");
		sb.append(s);
		if(i > 10 || s.indexOf("javax.swing") >= 0) {
		    sb.append(" and more...");
		    break;
		}
	    }
	    th = th.getCause();
	}
	sb.append("\n");
        return sb.toString();
    }

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JButton extractMemberButton;
    private javax.swing.JCheckBox forcedExtractChkBtn;
    private javax.swing.JCheckBox forcedRebuildChkBtn;
    private javax.swing.JList hedList;
    private javax.swing.JScrollPane hedListScrollPane;
    private MemberInfoPanel hedMemberTable1;
    private javax.swing.JButton jButton7;
    private javax.swing.JButton jButton8;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JLabel jLabel5;
    private javax.swing.JList jList1;
    private javax.swing.JList jList2;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JRadioButton jRadioButton1;
    private javax.swing.JRadioButton jRadioButton2;
    private javax.swing.JRadioButton jRadioButton3;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JScrollPane jScrollPane2;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JSplitPane jSplitPane2;
    private javax.swing.JSplitPane jSplitPane3;
    private javax.swing.JTabbedPane jTabbedPane2;
    private javax.swing.JTextArea jTextArea1;
    private javax.swing.JButton purgeDatButton;
    private javax.swing.JButton purgeMemberButton;
    private javax.swing.JButton rebuildDatButton;
    private javax.swing.JButton replaceButton;
    private javax.swing.JButton restoreDatButton;
    private javax.swing.JButton restoreMemberButton;
    private javax.swing.JScrollPane textAreaScrollPane;
    // End of variables declaration//GEN-END:variables

}
