/*
!=======================================================================
!
!  PROGRAM  PHASE-Viewer  (PHASE-Viewer 2014.01 ver.3.3.0)
!
!  Created on ----
!  AUTHOR(S): KOGA, Junichiro
!  File : InputInterface.java
!  
!  Contact address :  Phase System Consortium
!                     E-mail: phase_system@nims.go.jp URL https://azuma.nims.go.jp
!
!
!   Since 2002, this program set had been intensively developed as a part of the following 
!  national projects supported by the Ministry of Education, Culture, Sports, Science and
!  Technology (MEXT) of Japan; "Frontier Simulation Software for Industrial Science
!  (FSIS)" from 2002 to 2005, "Revolutionary Simulation Software (RSS21)" from 2006 to
!  2008. "Research and Development of Innovative Simulation Software (RISS)" from 2008
!  to 2013. These projects is lead by the Center for Research on Innovative Simulation 
!  Software (CISS), the Institute of Industrial Science (IIS), the University of Tokyo.
!   Since 2013, this program set has been further developed centering on PHASE System
!  Consortium. 
!   The activity of development of this program set has been supervised by Takahisa Ohno.
!
!=======================================================================
 */
package ciss.phase_viewer.inputinterface;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.Stack;
import java.util.Vector;

import org.apache.log4j.Logger;

import ciss.phase_viewer.inputinterface.filestate.FileStateObserver;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTable;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableColumns;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableSpec;
import ciss.phase_viewer.inputinterface.inputinterfacetable.InputInterfaceTableSpecManager;

public class InputInterface implements Serializable, Input {
    public static String __ROOT__ = "rootBlock";

    private InputInterfaceBlock list = new InputInterfaceBlock();

    private static Logger logger = Logger.getLogger(InputInterface.class
            .getName());

    private int blockDepth;

    private PrintWriter writer;

    private String indent = "    ";

    private int restartAt = 1;

    private Stack restartAts;

    private String[] defaultName;

    private String[] defaultValue;

    private String fileName;

    private InputInterfaceTableSpec[] specs;

    private InputInterfaceTableSpecManager manager;

    private Vector changeListeners = new Vector();

    private Vector blockChangeListeners = new Vector();

    private UndoRedo undoRedo;

    private boolean updateUndoStack = true;

    private boolean stateChanged = false;

    private boolean writeNullValue = false;

    private boolean genLockFileOnSave = false;

    public InputInterface() {
        list.add(null);
        list.setName("root block");
        undoRedo = new UndoRedo(this);
    }

    /**
     * 保存するとき, FileStateObserver.LOCK_FILEというファイルを作業ディレクトリーの下に作成するか否かを指定.
     * 
     * @param genLockFileOnSave
     *            trueの場合, 上記のようなファイルを作成する. これがある場合,
     *            FileStateObserverは登録されたリスナーに FileStateChangeを通知することはない.
     */
    public void setGenerateLockFileOnSave(boolean genLockFileOnSave) {
        this.genLockFileOnSave = genLockFileOnSave;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }

    public void setUpdateUndoStack(boolean updateUndoStack) {
        this.updateUndoStack = updateUndoStack;
    }

    /**
     * アンドゥ操作を行う. (UndoRedoオブジェクトのundo()メソッドを呼ぶだけ)
     */
    public void undo() {
        undoRedo.undo();
    }

    /**
     * リドゥ操作を行う. (UndoRedoオブジェクトのredo()メソッドを呼ぶだけ)
     */
    public void redo() {
        undoRedo.redo();
    }

    /**
     * undo/redoのスタックなどをクリアする.
     */
    protected void clearUndoRedo() {
        undoRedo.clear();
    }

    /**
     * このクラスに関連付けられたUndo/Redoに, 変更を通知されるリスナーを登録する.
     * 
     * @param listener
     *            登録したいUndoRedoListener.
     */
    public void addUndoRedoListener(UndoRedoListener listener) {
        undoRedo.addUndoRedoListener(listener);
    }

    private void blockChanged(String tag, InputInterfaceBlock block,
            InputInterfaceBlockChangeListener source) {
        InputInterfaceBlockChangeEvent event = new InputInterfaceBlockChangeEvent(
                tag, block, source, this);
        for (int i = 0; i < blockChangeListeners.size(); i++) {
            InputInterfaceBlockChangeListener listener = (InputInterfaceBlockChangeListener) blockChangeListeners
                    .elementAt(i);
            listener.inputInterfaceBlockChanged(event);
        }
    }

    private void entryChanged(String tag, InputInterfaceEntry entry,
            InputInterfaceEntryChangeListener source) {
        InputInterfaceEntryChangeEvent event = new InputInterfaceEntryChangeEvent(
                tag, entry, source, this);
        if (updateUndoStack) {
            // logger.debug("adding entry to undoRedo: "+tag+" "+entry);
            undoRedo.addEntry(tag, entry);
            stateChanged = true;
        }
        for (int i = 0; i < changeListeners.size(); i++) {
            InputInterfaceEntryChangeListener listener = (InputInterfaceEntryChangeListener) changeListeners
                    .elementAt(i);
            logger.debug(" foobar " + i + " called from "
                    + changeListeners.elementAt(i).getClass());
            listener.inputInterfaceEntryChanged(event);
        }
    }

    protected void initialize() {
        selectRoot();
        InputInterfaceBlock root = getCurrentBlock();
        root.init("root block");
        undoRedo.clear();
        for (int i = 0; i < changeListeners.size(); i++) {
            InputInterfaceEntryChangeListener listener = (InputInterfaceEntryChangeListener) changeListeners
                    .elementAt(i);
            listener.inputInterfaceInitializing();
        }
    }

    protected void intialized() {
        for (int i = 0; i < changeListeners.size(); i++) {
            InputInterfaceEntryChangeListener listener = (InputInterfaceEntryChangeListener) changeListeners
                    .elementAt(i);
            listener.inputInterfaceInitialized();
        }
    }

    public void addInputInterfaceEntryChangeListener(
            InputInterfaceEntryChangeListener listener) {
        // 重複して登録はしない.
        for (int i = 0; i < changeListeners.size(); i++) {
            if (changeListeners.get(i) == listener) {
                return;
            }
        }
        logger.debug("adding listener: " + listener.getClass());
        changeListeners.addElement(listener);
    }

    public void addInputInterfaceBlockChangeListener(
            InputInterfaceBlockChangeListener listener) {
        // 重複して登録はしない.
        for (int i = 0; i < blockChangeListeners.size(); i++) {
            if (blockChangeListeners.get(i) == listener) {
                return;
            }
        }
        blockChangeListeners.addElement(listener);
    }

    public void removeInputInterfaceEntryChangeListener(
            InputInterfaceEntryChangeListener listener) {
        changeListeners.remove(listener);
    }

    public void removeInputInterfaceBlockChangeListener(
            InputInterfaceBlockChangeListener listener) {
        blockChangeListeners.remove(listener);
    }

    /**
     * ブロック名からブロックを選択, 必要に応じてないブロックを作成. 「ブロック名デリミター」は'.'.
     * 
     * @param String
     *            blockName ブロック名;「フルパス」で指定する
     */
    public void selectAndCreateBlock(String blockName) {
        String[] array = blockName.split("\\.");
        if (array != null) {
            selectAndCreateBlock(array, array.length);
        }
    }

    /**
     * ブロック名配列からブロックを選択, 必要とあればないブロックは作成.
     * 
     * @param blockNames
     *            ブロック名配列. たとえば, foo.barというブロック名の場合, new String []
     *            {foo,bar}みたいな配列.
     * @param depth
     *            上記配列の内, いくつめまでに対してこの操作を行うか. もしブロック名配列の長さよりも大きい場合はブロック名配列が使われる.
     */
    public void selectAndCreateBlock(String[] blockNames, int depth) {
        int bdepth = depth;
        if (depth > blockNames.length) {
            bdepth = blockNames.length;
        }

        String blName = "";
        for (int i = 0; i < bdepth; i++) {
            String ident = blockNames[i];
            if (blName.trim().length() == 0) {
                blName = ident;
            } else {
                blName += "." + ident;
            }
            if (!selectBlock(ident)) {
                logger.debug("creating block: " + blName);
                addBlock(new InputInterfaceBlock(ident, blName));
            }
        }

    }

    /**
     * 指定されたブロックに存在するテーブルへの参照を返す.
     * 
     * @param blockName
     *            ほしいテーブルのブロック名(フルパスで指定すること)
     * @return テーブルへの参照.
     */
    public InputInterfaceTable getInputInterfaceTable(String blockName) {
        selectRoot();
        String[] fullName = blockName.split("\\.");
        selectAndCreateBlock(fullName, fullName.length);
        return getTable();
    }

    /**
     * 指定されたパスに存在するPrimitiveEntryへの参照を返す.
     * 
     * @param entryName
     *            ほしいPrimitiveEntryへのパス名(フルパスで指定すること)
     * @return 指定のInputInterfacePrimitiveEntryへの参照.
     */
    public InputInterfacePrimitiveEntry getInputInterfacePrimitiveEntry(
            String entryName) {
        selectRoot();
        String[] fullName = entryName.split("\\.");
        selectAndCreateBlock(fullName, fullName.length - 1);
        return getPrimitiveEntry(fullName[fullName.length - 1]);
    }

    private String getParentTag(String[] tag) {
        String ret = "";
        for (int i = 0; i < tag.length - 1; i++) {
            ret += tag[i];
        }
        return ret;
    }

    /**
     * 指定されたパスに存在するUnitEntryへの参照を返す.
     * 
     * @param entryName
     *            ほしいUnitへパス名(フルパスで指定すること)
     * @return 指定のInputInterfaceUnitへの参照.
     */
    public InputInterfaceUnits getInputInterfaceUnits(String unitName) {
        selectRoot();
        String[] fullName = unitName.split("\\.");
        selectAndCreateBlock(fullName, fullName.length - 1);
        return getUnits();
    }

    /**
     * ブロックを現ブロックの末尾に足す. selectがfalseの場合 足したあともそのブロックへの移動はしない.
     * 
     * @param block
     *            足されるブロック.
     * @param select
     *            親ブロックへ移動するかいなか.
     */
    public void addBlock(InputInterfaceBlock block, boolean select) {
        if (block.size() == 0) {
            block.addFirst(list);
        } else {
            block.set(0, list);
        }
        list.addLast(block);
        if (select) {
            selectBlock(block.getName(), null);
        }
    }

    /**
     * ブロックを現ブロックの末尾に足す. 足したあと, そのブロックへ移動する.
     * 
     * @param block
     *            足されるブロック.
     */
    public void addBlock(InputInterfaceBlock block) {
        if (block.size() == 0) {
            block.addFirst(list);
        } else {
            block.set(0, list);
        }
        list.addLast(block);
        selectBlock(block.getName(), null);
    }

    /**
     * エントリーを現ブロックの末尾に足す.
     * 
     * @param entry
     *            足されるエントリー.
     */
    private void addEntry(InputInterfaceEntry entry) {
        list.addLast(entry);
        int type = entry.getEntryType();

        if (type == InputInterfaceEntry.TABLE) {
            list.setHasTable(true);
            // if ( manager != null ) {
            // logger.debug("obtaining table spec...");
            // InputInterfaceTableSpec tspec =
            // manager.getTableSpec(list.getFullName());
            // if ( tspec != null ) {
            // logger.debug("obtaining columns");
            // InputInterfaceTableColumns [] cols = tspec.getTableColumns();
            // if ( cols != null ) {
            // logger.debug("adding columns...");
            // ((InputInterfaceTable)
            // entry).setInputInterfaceTableColumns(cols);
            // }
            // }
            // }
        }

        if (type == InputInterfaceEntry.DEFAULTS) {
            list.setHasDefaults(true);
        }

        if (type == InputInterfaceEntry.UNITS) {
            list.setHasUnits(true);
        }

    }

    /**
     * 既存のブロックを差し替えるメソッド. 直下のブロックをサーチする. ブロック名は重ならないことを仮定している. そのブロックが見つからない場合
     * 末尾に足しこまれる. InputInterfaceEntryChangeEventを登録されたリスナーに発行する場合はこっちを利用する.
     * 
     * @param block
     *            足しこみたいブロック.
     * @param caller
     *            呼び出し元.
     */
    public void replaceBlock(InputInterfaceBlock block,
            InputInterfaceBlockChangeListener caller) {
        boolean found = false;
        replaceBlock(block);
        blockChanged(block.getFullName(), block, caller);
    }

    /**
     * 既存のブロックを差し替えるメソッド. 直下のブロックをサーチする. ブロック名は重ならないことを仮定している. そのブロックが見つからない場合
     * 末尾に足しこまれる.
     * 
     * @param block
     *            足しこみたいブロック.
     */
    public void replaceBlock(InputInterfaceBlock block) {
        boolean found = false;
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data.isBlock()
                    && data.getName().trim().equalsIgnoreCase(block.getName())) {
                logger.debug("replacing block: " + data);
                list.remove(i);
                list.add(i, block);
                if (block.size() != 0) {
                    block.set(0, list);
                } else {
                    block.add(list);
                }
                found = true;
                break;
            }
        }
        if (!found) {
            addBlock(block, false);
        }
    }

    /**
     * 既存のエントリーを差し替えるメソッド. #tag, #default, #unitなどは一番はじめにみつかったものと差し替える.
     * 見つからなかった場合末尾に足しこむ.
     * 
     * @param entry
     *            足しこみたいエントリー.
     * @param source
     *            このメソッドの呼び出し元(null可).
     * @param callListener
     *            真の場合, リスナーのメソッドを呼ぶ.
     */
    public void replaceEntry(InputInterfaceEntry entry,
            InputInterfaceEntryChangeListener source, boolean callListener) {
        int type = entry.getEntryType();
        String name = entry.getName();
        for (int i = 0; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data != null && !data.isBlock()) {
                InputInterfaceEntry en = (InputInterfaceEntry) data;
                if (data.getName().trim().equalsIgnoreCase(name)
                        && type == en.getEntryType()) {
                    list.remove(i);
                    boolean badd = true;
                    if (type == InputInterfaceEntry.PRIMITIVE) {
                        InputInterfacePrimitiveEntry pentry = (InputInterfacePrimitiveEntry) entry;
                        badd = !(pentry.getValue() == null || pentry.getValue()
                                .trim().length() == 0);
                    } else if (type == InputInterfaceEntry.TABLE) {
                        InputInterfaceTable tentry = (InputInterfaceTable) entry;
                        badd = !(tentry.getTableData() == null || tentry
                                .getTableData().size() == 0);
                    }
                    if (badd) {
                        if (type == InputInterfaceEntry.UNITS
                                || type == InputInterfaceEntry.DEFAULTS) {
                            logger.debug("adding entry at top of list for units & defaults.");
                            list.add(1, entry);
                        } else {
                            logger.debug("adding entry at bottom of list for primitive entries & tables.");
                            list.addLast(entry);
                        }
                        logger.debug("replacing entry..." + list.getFullName()
                                + "." + entry.getName());
                        if (callListener && source != null) {
                            entryChanged(list.getFullName(), entry, source);
                        }
                    }
                    return;
                }
            }
        }
        logger.debug("adding entry..." + list.getFullName() + "."
                + entry.getName());
        addEntry(entry);

        if (callListener && source != null) {
            entryChanged(list.getFullName(), entry, source);
        }
    }

    /**
     * 既存のエントリーを差し替えるメソッド. #tag, #default, #unitなどは一番はじめにみつかったものと差し替える.
     * 見つからなかった場合末尾に足しこむ. 呼び出し元はnullが使われ, また リスナーのメソッドは呼ばれる.
     * 
     * @param entry
     *            足しこみたいエントリー.
     */
    public void replaceEntry(InputInterfaceEntry entry) {
        replaceEntry(entry, null, true);
    }

    /**
     * 既存のエントリーを差し替えるメソッド. #tag, #default, #unitなどは一番はじめにみつかったものと差し替える.
     * 見つからなかった場合末尾に足しこむ. リスナーのメソッドは呼ばれる.
     * 
     * @param entry
     *            足しこみたいエントリー.
     * @param source
     *            呼び出し元のInputInterfaceEntryChangeListener.
     */
    public void replaceEntry(InputInterfaceEntry entry,
            InputInterfaceEntryChangeListener source) {
        replaceEntry(entry, source, true);
    }

    /**
     * 既存のエントリーを削除するメソッド. 見つからなかった場合は何もしない.
     * 
     * @param entry
     *            削除したいエントリー(ブロックはあらかじめ選択済みである必要がある.
     * @param source
     *            このメソッドの呼び出し元.
     */
    public void removeEntry(InputInterfaceEntry entry,
            InputInterfaceEntryChangeListener source) {
        removeEntry(entry, source, true);
    }

    /**
     * 既存のエントリーを削除するメソッド. 見つからなかった場合は何もしない. sourceとしては, nullが渡される. また,
     * リスナーのメソッドが呼ばれる.
     * 
     * @param entry
     *            削除したいエントリー(ブロックはあらかじめ選択済みである必要がある.
     */
    public void removeEntry(InputInterfaceEntry entry) {
        removeEntry(entry, null, true);
    }

    /**
     * 既存のエントリーを削除するメソッド. 見つからなかった場合は何もしない.
     * 
     * @param entry
     *            削除したいエントリー.
     * @param source
     *            このメソッドの呼び出し元.
     * @param callListener
     *            真の場合, リスナーのメソッドを呼ぶ.
     */
    public void removeEntry(InputInterfaceEntry entry,
            InputInterfaceEntryChangeListener source, boolean callListener) {
        int type = entry.getEntryType();
        String name = entry.getName();
        for (int i = 0; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data != null && !data.isBlock()) {
                InputInterfaceEntry en = (InputInterfaceEntry) data;
                if (data.getName().trim().equalsIgnoreCase(name)
                        && type == en.getEntryType()) {
                    list.remove(i);
                    if (callListener) {
                        entryChanged(list.getFullName(), entry, source);
                    }
                    return;
                }
            }
        }
    }

    /**
     * 指定のInputInterfaceBlockを削除する.
     * 
     * @param block
     *            削除したいInputInterfaceBlock.
     */
    public void removeBlock(InputInterfaceBlock block) {
        list.remove(block);
    }

    /**
     * 名前から削除すべきブロックを見つけようとする.
     * 
     * @param name
     *            削除したいブロックのブロック名
     */
    public void removeBlock(String name) {
        for (int i = 0; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data != null && data.isBlock() && data.getName().equals(name)) {
                ((InputInterfaceBlock) data).remove(0);
                list.remove(data);
            }
        }
    }

    /**
     * 親ブロックを選択するメソッド. ルートブロックの場合は偽を返す.
     * 
     * @return ルートブロックの場合偽.
     */
    public boolean selectParentBlock() {
        if (list.get(0) != null) {
            list = (InputInterfaceBlock) list.get(0);
            return true;
        }
        return false;
    }

    /**
     * ブロックを選択する. ブロック名の区切り文字は".". 例えば, foo.bar.hogehogeというブロック名を渡した場合, foo{
     * bar{ hogehoge{ ...... } } } という形式の入力ファイルの.....の部分を選択することができる.
     * 
     * @param blockName
     *            選択したいブロック名.
     * @return 指定のブロックが見つからない場合偽.
     */
    public boolean selectBlock(String blockName) {
        return selectBlock(blockName, "\\.");
    }

    /**
     * ブロックを選択する. ブロックの名前は, デフォルトでは"."で区切られていると みなす. 区切り文字を指定することも可能.
     * 区切り文字がnullか空白文字の場合, 区切られていない文字列と見做す.
     * 
     * @param blockName
     *            選択したいブロック名 (InputInterface.__ROOT__ を渡せばルートブロック).
     * @param delimiter
     *            区切り文字
     * @return 指定のブロックが見つからない場合偽.
     */
    public boolean selectBlock(String blockName, String delimiter) {
        if (blockName == __ROOT__) {
            selectRoot();
            return true;
        }
        String[] decomp = null;

        if (delimiter == null || delimiter.trim().length() == 0) {
            decomp = new String[1];
            decomp[0] = blockName;
        } else {
            decomp = blockName.split(delimiter);
            if (decomp == null) {
                return false;
            }
        }

        // we assume that blockName is in full path if its decomposed
        // representation has more than 1 element.
        if (decomp.length > 1) {
            selectRoot();
        }

        for (int i = 0; i < decomp.length; i++) {
            String ident = decomp[i];
            if (!selectBlockSub(ident)) {
                logger.debug("couldn't select block: " + ident);
                return false;
            } else {
                logger.debug("selected block: " + ident);
            }
        }
        return true;
    }

    private boolean selectBlockSub(String blockName) {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            logger.debug("name of data .... " + data.getName());
            if (data.isBlock()) {
                String name = data.getName();
                if (name.trim().equalsIgnoreCase(blockName)) {
                    list = (InputInterfaceBlock) data;
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 現在のブロックへの参照を返す.
     * 
     * @return 現在のブロック.
     */
    public InputInterfaceBlock getCurrentBlock() {
        return this.list;
    }

    /**
     * 指定のPrimitive entryを返す.
     * 
     * @param name
     *            欲しいprimitive entryの名前.
     * @return primitive entryへの参照. 見つからなかった場合新規primitive entryオブジェクト.
     */
    public InputInterfacePrimitiveEntry getPrimitiveEntry(String name) {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            logger.debug("at getPrimitiveEntry: got data: " + data.getName());
            if (data.getName().trim().equalsIgnoreCase(name) && !data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.PRIMITIVE) {
                    return (InputInterfacePrimitiveEntry) entry;
                }
            }
        }
        InputInterfacePrimitiveEntry ent = new InputInterfacePrimitiveEntry();
        ent.setName(name);
        addEntry(ent);
        return ent;
    }

    /**
     * 現ブロック内の全てのPrimitive entryを取得.
     * 
     * @return InputInterfacePrimitiveEntry型の配列. Primitive entryが見つからなかった場合 null
     *         を返す.
     */
    public InputInterfacePrimitiveEntry[] getAllPrimitiveEntries() {
        Vector entries = new Vector();
        InputInterfacePrimitiveEntry[] ret;

        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.PRIMITIVE) {
                    entries.addElement((InputInterfacePrimitiveEntry) entry);
                }
            }
        }

        if (entries.size() == 0) {
            return null;
        }
        ret = new InputInterfacePrimitiveEntry[entries.size()];
        for (int i = 0; i < entries.size(); i++) {
            ret[i] = (InputInterfacePrimitiveEntry) entries.get(i);
        }

        return ret;
    }

    /**
     * 指定のtable entryを返す. ブロック内で見つかった最初のテーブルを返す.
     * 
     * @return table entryへの参照.
     */
    public InputInterfaceTable getTable() {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.TABLE) {
                    return (InputInterfaceTable) entry;
                }
            }
        }

        logger.debug("getCurrentBlock().getFullName(): "
                + getCurrentBlock().getFullName());
        InputInterfaceTableColumns[] cols = null;
        if (manager != null) {
            logger.debug("obtaining table spec...");
            InputInterfaceTableSpec tspec = manager.getTableSpec(list
                    .getFullName());
            if (tspec != null) {
                logger.debug("obtaining columns");
                cols = tspec.getTableColumns();
            }
        }
        InputInterfaceTable ta = new InputInterfaceTable(getCurrentBlock()
                .getName(), null, cols, new Vector());
        replaceEntry(ta);
        return ta;
    }

    /**
     * 指定のdefault entryを返す. ブロック内で見つかった最初のテーブルを返す.
     * 
     * @return default entryへの参照.
     */
    public InputInterfaceDefaults getDefaults() {
        if (!list.hasDefaults())
            return null;
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.DEFAULTS) {
                    return (InputInterfaceDefaults) entry;
                }
            }
        }
        return null;
    }

    /**
     * 指定のdefault entryを返す. ブロック内で見つかった最初のテーブルを返す.
     * 
     * @return default entryへの参照.
     */
    public InputInterfaceDefaults getDefaults(String name) {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data.getName().trim().equalsIgnoreCase(name) && !data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.DEFAULTS) {
                    return (InputInterfaceDefaults) entry;
                }
            }
        }
        return new InputInterfaceDefaults();
    }

    /**
     * unit entryへの参照を取得する.
     * 
     * @return units entryへの参照.
     */
    public InputInterfaceUnits getUnits() {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.UNITS) {
                    return (InputInterfaceUnits) entry;
                }
            }
        }
        return new InputInterfaceUnits();
    }

    /**
     * ルートブロックを選択するメソッド.
     */
    public void selectRoot() {
        if (selectParentBlock()) {
            selectRoot();
        }
    }

    /**
     * ルートブロックへの参照を返す.
     * 
     * @return ルートブロックへの参照.
     */
    public InputInterfaceBlock getRootList() {
        this.selectRoot();
        return this.list;
    }

    /**
     * 状態が変更したか否かを返す.
     * 
     * @return 状態の変更を保持する真偽値.
     */
    public boolean stateChanged() {
        return stateChanged;
    }

    /**
     * 強制的にstateを設定する (使用にあたっては要注意).
     * 
     * @param stateChanged
     *            変更したいstate.
     */
    public void setState(boolean stateChanged) {
        this.stateChanged = stateChanged;
    }

    private String string;

    /**
     * このInputInterfaceオブジェクトを, ファイルに書き出すのではなくStringで返す.
     * 
     * @return InputInterfaceオブジェクトのString
     */
    public String toString() {
        logger.debug("obtaining string representation of 'inputinterface'...");
        blockDepth = 0;
        this.selectRoot();
        restartAts = new Stack();
        restartAt = 1;

        writer = null;
        string = "";
        saveSub();
        return string;
    }

    /**
     * このオブジェクトが保持しているファイルへ保存.
     */
    public void save() {
        saveTo(new File(fileName));
    }

    /**
     * このオブジェクトが保持しているファイルへ保存.
     * 
     * @param recreate
     *            この保存によってInputInterfaceの再構築を行うべきか否か.
     */
    public void save(boolean recreate) {
        saveTo(new File(fileName), recreate);
    }

    public void saveTo(File file) {
        saveTo(file, false);
    }

    protected boolean block = false;

    private void deb(InputInterfaceBlock block) {
        if (!hasData(block))
            return;

        blockDepth++;
        String ws = getWhiteSpace(blockDepth);
        logger.debug(ws + block.getName() + "{");

        if (writer != null)
            writer.println(ws + block.getName() + "{");
        else
            string += ws + block.getName() + "{"
                    + System.getProperty("line.separator");

        if (block.hasUnits())
            writeUnits(block, blockDepth);

        if (block.hasDefaults())
            writeDefaults(block, blockDepth);

        for (int i = 1; i < block.size(); i++) {
            InputInterfaceData dat = (InputInterfaceData) block.get(i);
            if (dat.isBlock()) {
                deb((InputInterfaceBlock) dat);
            } else if (dat instanceof InputInterfaceEntry) {
                String foo = toString((InputInterfaceEntry) dat);
                if (foo == null)
                    continue;
                logger.debug(foo);
                if (writer != null)
                    writer.println(foo);
                else
                    string += foo + System.getProperty("line.separator");
            }
        }

        blockDepth--;
        logger.debug(ws + "}");

        if (writer != null)
            writer.println(ws + "}");
        else
            string += ws + "}" + System.getProperty("line.separator");
    }

    /**
     * 引数のブロックが, 下位のものも含めてデータを持たない場合false, それ以外の場合はtrueを返す.
     * 
     * @param block
     *            調べたいブロック
     * @return 現在選択中のブロックが値を持っているかいなか.
     */
    private boolean hasData(InputInterfaceBlock block) {
        loop: for (int i = 1; i < block.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) block.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.TABLE) {
                    boolean[] val = ((InputInterfaceTable) entry)
                            .getValidColumns();
                    boolean bl = false;
                    for (int ii = 0; ii < val.length; ii++) {
                        if (val[ii]) {
                            bl = true;
                        }
                    }
                    if (!bl) {
                        continue loop;
                    }
                } else if (entry.getEntryType() == InputInterfaceEntry.PRIMITIVE) {
                    InputInterfacePrimitiveEntry pent = (InputInterfacePrimitiveEntry) entry;
                    if (pent.getValue() == null
                            || pent.getValue().trim().length() == 0) {
                        continue loop;
                    }
                } else if (entry.getEntryType() == InputInterfaceEntry.UNITS) {
                    continue loop;
                }
                return true;
            } else {
                boolean bl = hasData((InputInterfaceBlock) data);
                if (bl)
                    return true;
            }
        }
        return false;
    }

    /**
     * 指定のファイルへ書き出す.
     * 
     * @param file
     *            書き出したいファイル.
     * @param recreate
     *            この保存によってInputInterfaceの再構築を行うべきか否か.
     */
    public void saveTo(File file, boolean recreate) {

        // if ( genLockFileOnSave )
        FileStateObserver.generateLockFile(file.getParentFile());

        block = true;
        blockDepth = 0;
        this.selectRoot();

        restartAts = new Stack();
        restartAt = 1;
        logger.info("saving input to: " + file);

        try {
            writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            for (int i = 1; i < list.size(); i++) {
                InputInterfaceData data = (InputInterfaceData) list.get(i);
                if (data.isBlock())
                    deb((InputInterfaceBlock) data);
            }
            // saveSub();
            logger.info("input saved.");
        } catch (Exception exc) {
            logger.warn("file in use?");
            // logger.error("failed write to: " + file);
            exc.printStackTrace();
        } finally {
            writer.close();
            iSavedIt = !recreate;
        }

        this.selectRoot();
        block = false;
        stateChanged = false;
        try {
            Thread.sleep(100);
        } catch (Exception e) {
        }
        // if ( genLockFileOnSave )
        FileStateObserver.removeLockFile(file.getParentFile());

    }

    /**
     * 現在選択中のブロックが, 下位のものも含めてデータを持たない場合false, それ以外の場合はtrueを返す. 現在,
     * 面倒なので一階層下までしか見ない. (wavefunction_solver.solvers やcharge_mixing.cmix対応)
     * これでも困る場合は より汎用的にしよう.
     * 
     * @return 現在選択中のブロックが値を持っているかいなか.
     */
    private boolean currBlockHasData() {
        loop: for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                if (entry.getEntryType() == InputInterfaceEntry.TABLE) {
                    boolean[] val = ((InputInterfaceTable) entry)
                            .getValidColumns();
                    boolean bl = false;
                    for (int ii = 0; ii < val.length; ii++) {
                        if (val[ii]) {
                            bl = true;
                        }
                    }
                    if (!bl) {
                        continue loop;
                    }
                } else if (entry.getEntryType() == InputInterfaceEntry.PRIMITIVE) {
                    InputInterfacePrimitiveEntry pent = (InputInterfacePrimitiveEntry) entry;
                    if (pent.getValue() == null
                            || pent.getValue().trim().length() == 0) {
                        continue loop;
                    }
                } else if (entry.getEntryType() == InputInterfaceEntry.UNITS) {
                    continue loop;
                }
                return true;
            }
            selectBlock(data.getName());
            if (currBlockHasData()) {
                selectParentBlock();
                return true;
            } else {
                selectParentBlock();
                // return false;
            }
        }
        return false;
    }

    private void saveSub() {
        try {
            for (int i = restartAt; i < list.size(); i++) {
                InputInterfaceData data = (InputInterfaceData) list.get(i);
                String name = data.getName();
                if (data.isBlock()) {
                    blockDepth++;
                    selectBlock(name, null);
                    Integer ii = new Integer(i + 1);
                    restartAts.push(ii);
                    logger.debug("entering block: " + name);
                    logger.debug("pushed: " + ii);
                    boolean bool = currBlockHasData();
                    if (!bool) {
                        endBlockWrite();
                        saveSub();
                        break;
                    }

                    writeBlock((InputInterfaceBlock) data);
                    if (((InputInterfaceBlock) data).hasUnits()) {
                        writeUnits();
                    }
                    if (((InputInterfaceBlock) data).hasDefaults()) {
                        writeDefaults();
                    }
                    restartAt = 1;
                    saveSub();
                    break;
                } else {
                    InputInterfaceEntry entry = (InputInterfaceEntry) data;
                    // System.out.println("block depth: "+blockDepth);
                    // System.out.println("entry now: "+data.getName());
                    if (blockDepth == 0 && data.getName().trim().length() == 0) {
                        logger.debug("something is wrong....");
                        break;
                    }
                    writeEntry(entry);
                    if (i == list.size() - 1) {
                        endBlockWrite();
                        saveSub();
                        break;
                    }
                }
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }

        // finally, set the "stateChanged" variable to false.
    }

    private void endBlockWrite() {
        logger.debug("exiting block: " + list.getName());
        if (currBlockHasData()) {
            writeBlockEnd();
        }
        if (selectParentBlock()) {
            logger.debug("re-entering block: " + list.getName());
        } else {
            return;
        }
        blockDepth--;
        logger.debug("blockDepth at this point: " + blockDepth);
        restartAt = ((Integer) restartAts.pop()).intValue();
        logger.debug("poped: " + restartAt);
        if (restartAt == list.size()) {
            endBlockWrite();
        }
    }

    private void writeBlock(InputInterfaceBlock block) {
        String whiteSpace = getWhiteSpace();
        if (block.getName().equals("wavefunction_solver")) {
            logger.debug("getTags(): ");
            String[] tgs = getTags();
            logger.debug("num tags: " + tgs.length);
            for (int i = 0; i < tgs.length; i++) {
                logger.debug("tag: " + tgs[i]);
            }
        }
        if (writer != null) {
            writer.println(whiteSpace + block.getName() + "{");
        } else {
            string += whiteSpace + block.getName() + "{"
                    + System.getProperty("line.separator");
        }
    }

    private String toString(InputInterfaceEntry entry) {
        String whiteSpace = getWhiteSpace(blockDepth);
        int type = entry.getEntryType();
        if (entry.ignoreMe()) {
            return null;
        }
        if (type == InputInterfaceEntry.PRIMITIVE) {
            InputInterfacePrimitiveEntry primitive = (InputInterfacePrimitiveEntry) entry;
            String name = primitive.getName();
            // String value = primitive.getValue();
            String value = primitive.getValueDelimitedBy();
            if (writeDelimitedByColon) {
                value = primitive.getValue();
            }
            if (primitive.isDoubleQuoted()) {
                value = "\"" + value + "\"";
            }

            boolean writeThis = false;
            if (writeNullValue) {
                writeThis = name != null && name.trim().length() != 0
                        && value != null;
            } else {
                writeThis = name != null && name.trim().length() != 0
                        && value != null && value.trim().length() != 0;
            }

            if (writeThis)
                return whiteSpace + indent + name + " = " + value + " "
                        + primitive.getUnit();
        } else if (type == InputInterfaceEntry.TABLE) {
            InputInterfaceTable table = (InputInterfaceTable) entry;
            String[] idents = table.getColumnIdentifiers();

            String identString = whiteSpace + indent + "#tag";
            boolean[] bcols = table.getValidColumns();
            if (idents != null) {
                for (int i = 0; i < idents.length; i++) {
                    if (bcols[i]) {
                        identString += " " + idents[i];
                    }
                }
                Vector tableData = table.getTableData();
                String dataString = whiteSpace + indent;

                InputInterfaceTableSpec spec = null;
                try {
                    spec = manager.getTableSpec(list.getFullName());
                } catch (Exception exc) {
                }

                for (int i = 0; i < tableData.size(); i++) {
                    String[] row = (String[]) tableData.get(i);
                    if (InputInterfaceTable.isValidRow(row)) {
                        for (int j = 0; j < row.length; j++) {
                            if (bcols[j]) {
                                String dat = row[j];
                                if (spec != null
                                        && spec.getTableColumn(idents[j])
                                                .isFirstLetterCaps()) {
                                    dat = Utils.firstLetterCaps(dat);
                                }
                                if (row[j] != null && row[j].length() != 0) {
                                    dataString += " " + dat;
                                } else {
                                    dataString += " " + "*"; // "*" is the
                                    // input-interface
                                    // default
                                    // string.
                                }
                            }
                        }
                        if (i < tableData.size() - 1) {
                            dataString += System.getProperty("line.separator")
                                    + whiteSpace + indent;
                        }
                    }

                }
                return identString + System.getProperty("line.separator")
                        + dataString;
            }
        }
        return null;
    }

    private void writeEntry(InputInterfaceEntry entry) {
        String whiteSpace = getWhiteSpace();
        int type = entry.getEntryType();
        if (entry.ignoreMe()) {
            return;
        }
        if (type == InputInterfaceEntry.PRIMITIVE) {
            InputInterfacePrimitiveEntry primitive = (InputInterfacePrimitiveEntry) entry;
            String name = primitive.getName();
            // String value = primitive.getValue();
            String value = primitive.getValueDelimitedBy();
            if (writeDelimitedByColon) {
                value = primitive.getValue();
            }
            if (primitive.isDoubleQuoted()) {
                value = "\"" + value + "\"";
            }

            boolean writeThis = false;
            if (writeNullValue) {
                writeThis = name != null && name.trim().length() != 0
                        && value != null;
            } else {
                writeThis = name != null && name.trim().length() != 0
                        && value != null && value.trim().length() != 0;
            }

            if (writeThis) {
                if (writer != null) {
                    writer.println(whiteSpace + indent + name + " = " + value
                            + " " + primitive.getUnit());
                } else {
                    string += whiteSpace + indent + name + " = " + value + " "
                            + primitive.getUnit()
                            + System.getProperty("line.separator");
                }
            }
        } else if (type == InputInterfaceEntry.TABLE) {
            InputInterfaceTable table = (InputInterfaceTable) entry;
            String[] idents = table.getColumnIdentifiers();

            String identString = whiteSpace + indent + "#tag";
            boolean[] bcols = table.getValidColumns();
            if (idents != null) {
                for (int i = 0; i < idents.length; i++) {
                    if (bcols[i]) {
                        identString += " " + idents[i];
                    }
                }
                Vector tableData = table.getTableData();
                String dataString = whiteSpace + indent;

                InputInterfaceTableSpec spec = null;
                try {
                    spec = manager.getTableSpec(list.getFullName());
                } catch (Exception exc) {
                }

                for (int i = 0; i < tableData.size(); i++) {
                    String[] row = (String[]) tableData.get(i);
                    if (InputInterfaceTable.isValidRow(row)) {
                        for (int j = 0; j < row.length; j++) {
                            if (bcols[j]) {
                                String dat = row[j];
                                if (spec != null
                                        && spec.getTableColumn(idents[j])
                                                .isFirstLetterCaps()) {
                                    dat = Utils.firstLetterCaps(dat);
                                }
                                if (row[j] != null && row[j].length() != 0) {
                                    dataString += " " + dat;
                                } else {
                                    dataString += " " + "*"; // "*" is the
                                    // input-interface
                                    // default
                                    // string.
                                }
                            }
                        }
                        if (i < tableData.size() - 1) {
                            dataString += System.getProperty("line.separator")
                                    + whiteSpace + indent;
                        }
                    }

                }
                if (writer != null) {
                    writer.println(identString
                            + System.getProperty("line.separator") + dataString);
                } else {
                    string += identString
                            + System.getProperty("line.separator") + dataString
                            + System.getProperty("line.separator");
                }
            }
        }
    }

    private void writeUnits(InputInterfaceBlock block, int bld) {
        String whiteSpace = getWhiteSpace(bld);
        for (int i = 1; i < block.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) block.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.UNITS) {
                    InputInterfaceUnits units = (InputInterfaceUnits) entry;
                    String[] un = units.getUnits();
                    String unitString = whiteSpace + indent + "#units";
                    for (int j = 0; j < un.length; j++) {
                        unitString += " " + un[j];
                    }
                    if (writer != null) {
                        writer.println(unitString);
                    } else {
                        string += unitString
                                + System.getProperty("line.separator");
                    }
                }
            }
        }
    }

    private void writeDefaults(InputInterfaceBlock block, int bld) {
        String whiteSpace = getWhiteSpace(bld);
        for (int i = 1; i < block.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) block.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.DEFAULTS) {
                    InputInterfaceDefaults defs = (InputInterfaceDefaults) entry;
                    String[] de = defs.getDefaults();
                    String defString = whiteSpace + indent + "#default";
                    for (int j = 0; j < de.length; j++) {
                        defString += " " + de[j];
                        if (j < de.length - 1) {
                            defString += ",";
                        }
                    }
                    if (writer != null) {
                        writer.println(defString);
                    } else {
                        string += defString
                                + System.getProperty("line.separator");
                    }
                }
            }
        }
    }

    private void writeUnits() {
        String whiteSpace = getWhiteSpace();
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.UNITS) {
                    InputInterfaceUnits units = (InputInterfaceUnits) entry;
                    String[] un = units.getUnits();
                    String unitString = whiteSpace + indent + "#units";
                    for (int j = 0; j < un.length; j++) {
                        unitString += " " + un[j];
                    }
                    if (writer != null) {
                        writer.println(unitString);
                    } else {
                        string += unitString
                                + System.getProperty("line.separator");
                    }
                }
            }
        }
    }

    private void writeDefaults() {
        String whiteSpace = getWhiteSpace();
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.DEFAULTS) {
                    InputInterfaceDefaults defs = (InputInterfaceDefaults) entry;
                    String[] de = defs.getDefaults();
                    String defString = whiteSpace + indent + "#default";
                    for (int j = 0; j < de.length; j++) {
                        defString += " " + de[j];
                        if (j < de.length - 1) {
                            defString += ",";
                        }
                    }
                    if (writer != null) {
                        writer.println(defString);
                    } else {
                        string += defString
                                + System.getProperty("line.separator");
                    }
                }
            }
        }
    }

    private void writeBlockEnd() {
        if (list.get(0) != null) {
            if (writer != null) {
                writer.println(getWhiteSpace() + "}");
            } else {
                string += getWhiteSpace() + "}"
                        + System.getProperty("line.separator");
            }
        }
    }

    private String getWhiteSpace(int depth) {
        String ret = "";
        for (int i = 0; i < depth - 1; i++)
            ret += indent;
        return ret;
    }

    private String getWhiteSpace() {
        String string = new String();
        for (int i = 0; i < blockDepth - 1; i++) {
            string += indent;
        }
        return string;
    }

    /**
     * リストの内容をスキャンし, 標準出力へ書き出す.
     */
    public void scanList() {
        this.selectRoot();
        restartAts = new Stack();
        restartAt = 1;
        logger.debug("scanning list ...");
        this.scan();
    }

    /**
     * 現ブロックのタグ名全てを取得する.
     * 
     * @return タグ名のString配列; タグがない場合nullを返す.
     */
    public String[] getTags() {
        String[] ret = null;
        Vector names = new Vector();
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            names.addElement(data.getName());
        }
        if (names.size() > 0) {
            ret = new String[names.size()];
            for (int i = 0; i < names.size(); i++) {
                ret[i] = (String) names.get(i);
            }
        }

        return ret;
    }

    /**
     * 現ブロック中の全エントリー(ブロック, PrimitiveEntryに限らず)の配列を返す.
     * 
     * @return 現ブロックに存在する, 全エントリーの配列.
     */
    public InputInterfaceData[] getAllEntries() {
        InputInterfaceData[] ret = new InputInterfaceData[list.size() - 1];
        for (int i = 1; i < list.size(); i++) {
            ret[i - 1] = (InputInterfaceData) list.get(i);
        }
        return ret;
    }

    /**
     * 現ブロックのタグ名の内, ブロックのもののみ返す.
     * 
     * @return ブロックのタグ名のString配列; タグがない場合nullを返す.
     */
    public String[] getBlockTags() {
        String[] ret = null;
        Vector names = new Vector();
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data.isBlock()) {
                names.addElement(data.getName());
            }
        }
        if (names.size() > 0) {
            ret = new String[names.size()];
            for (int i = 0; i < names.size(); i++) {
                ret[i] = (String) names.get(i);
            }
        }
        return ret;
    }

    /**
     * ブロックを取得; 見つからなかった場合nullを返す.
     * 
     * @param name
     *            ブロック名; 相対パスで!
     */
    public InputInterfaceBlock getBlock(String name) {
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (data.isBlock() && data.getName().trim().equalsIgnoreCase(name)) {
                return (InputInterfaceBlock) data;
            }
        }
        return null;
    }

    /**
     * Blockの数を返す.
     * 
     * @return 現ブロックの下にあるブロックの数
     */
    public int getBlockCount() {
        String[] blocks = getBlockTags();
        if (blocks == null) {
            return 0;
        }
        return blocks.length;
    }

    /**
     * PrimitiveEntryの数を返す.
     * 
     * @return 現ブロックの下にあるPrimitiveEntryの数
     */
    public int getPrimitiveEntryCount() {
        InputInterfacePrimitiveEntry[] pes = getAllPrimitiveEntries();
        if (pes == null) {
            return 0;
        }
        return pes.length;
    }

    /**
     * 現ブロックのタグ名の内, Entryのもののみ返す.
     * 
     * @return ブロックのタグ名のString配列; タグがない場合nullを返す.
     */
    public String[] getEntryTags() {
        String[] ret = null;
        Vector names = new Vector();
        for (int i = 1; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            if (!data.isBlock()) {
                names.addElement(data.getName());
            }
        }
        if (names.size() > 0) {
            ret = new String[names.size()];
            for (int i = 0; i < names.size(); i++) {
                ret[i] = (String) names.get(i);
            }
        }
        return ret;
    }

    private void scan() {
        scan(false);
    }

    private void scan(InputInterfaceBlock block, boolean replace) {
        String blname = block.getFullName();
        for (int i = 1; i < block.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) block.get(i);
            String name = data.getName();
            if (data.isBlock()) {
                scan((InputInterfaceBlock) data, replace);
            } else {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.PRIMITIVE) {
                    InputInterfacePrimitiveEntry primitive = (InputInterfacePrimitiveEntry) entry;
                    logger.debug("    token: ");
                    logger.debug("        name: " + primitive.getName());
                    logger.debug("        value: " + primitive.getValue());
                    logger.debug("        unit: " + primitive.getUnit());
                } else if (type == InputInterfaceEntry.TABLE) {
                    InputInterfaceTable table = (InputInterfaceTable) entry;
                    String[] tableIdentifiers = table.getTableIdentifiers();
                    Vector tableData = table.getTableData();
                    if (replace && block.hasDefaults()) {
                        tableIdentifiers = getUniqueIdentifiers(defaultName,
                                tableIdentifiers);
                        table.setTableIdentifiers(tableIdentifiers);
                        expandTableData(tableData, tableIdentifiers.length);
                        Vector replaceVector = new Vector();
                        for (int j = 0; j < tableIdentifiers.length; j++) {
                            for (int jj = 0; jj < defaultName.length; jj++) {
                                if (tableIdentifiers[j].trim()
                                        .equalsIgnoreCase(
                                                defaultName[jj].trim())) {
                                    replaceVector.addElement(new String[] {
                                            Integer.toString(j),
                                            defaultValue[jj] });
                                }
                            }
                        }
                        for (int ii = 0; ii < tableData.size(); ii++) {
                            String[] cols = (String[]) tableData.get(ii);
                            for (int j = 0; j < replaceVector.size(); j++) {
                                String[] rep = (String[]) replaceVector.get(j);
                                int irep = Integer.parseInt(rep[0]);
                                if (cols[irep].trim().equals("*")) {
                                    cols[irep] = rep[1];
                                }
                            }
                        }
                    }
                    logger.debug("    table: ");
                    logger.debug("        ident: ");
                    logger.debug("            ");
                    for (int ii = 0; ii < tableIdentifiers.length; ii++) {
                        logger.debug("    " + tableIdentifiers[ii]);
                    }
                    logger.debug("        value:");
                    for (int ii = 0; ii < tableData.size(); ii++) {
                        String[] cols = (String[]) tableData.get(ii);
                        for (int j = 0; j < cols.length; j++) {
                            logger.debug("    " + cols[j]);
                        }
                    }

                    if (manager != null) {
                        logger.debug("obtaining table spec...");
                        InputInterfaceTableSpec tspec = manager
                                .getTableSpec(block.getFullName());
                        if (tspec != null) {
                            logger.debug("obtaining columns");
                            InputInterfaceTableColumns[] cols = tspec
                                    .getTableColumns();
                            if (cols != null) {
                                logger.debug("adding columns...");
                                ((InputInterfaceTable) entry)
                                        .setInputInterfaceTableColumns(cols);
                            }
                        }
                    }
                } else if (type == InputInterfaceEntry.DEFAULTS) {
                    InputInterfaceDefaults defs = (InputInterfaceDefaults) entry;
                    logger.debug("    defaults:");
                    String[] de = defs.getDefaults();
                    if (replace) {
                        defaultName = new String[de.length];
                        defaultValue = new String[de.length];
                    }
                    for (int ii = 0; ii < de.length; ii++) {
                        logger.debug("            default no." + ii + ": "
                                + de[ii]);
                        if (replace) {
                            String[] tmp = de[ii].split("=");
                            if (tmp[0] != null) {
                                defaultName[ii] = tmp[0].trim();
                            } else {
                                defaultName[ii] = "";
                            }
                            if (tmp[1] != null) {
                                defaultValue[ii] = tmp[1].trim();
                            } else {
                                defaultValue[ii] = "";
                            }
                        }
                    }
                } else if (type == InputInterfaceEntry.UNITS) {
                    InputInterfaceUnits units = (InputInterfaceUnits) entry;
                    logger.debug("    units:");
                    String[] un = units.getUnits();
                    for (int ii = 0; ii < un.length; ii++) {
                        logger.debug("            unit no." + ii + ": "
                                + un[ii]);
                    }
                }
                entryChanged(blname, entry, null);
            }
        }
    }

    private void scan(boolean replace) {
        for (int i = restartAt; i < list.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) list.get(i);
            String name = data.getName();
            if (data.isBlock()) {
                selectBlock(name, null);
                logger.debug("entering block: " + name);
                logger.debug("full name of this block: "
                        + ((InputInterfaceBlock) data).getFullName());
                Integer ii = new Integer(i + 1);
                restartAts.push(ii);
                if (((InputInterfaceBlock) data).hasTable()) {
                    logger.debug("this block has a table...");
                }
                if (((InputInterfaceBlock) data).hasDefaults()) {
                    logger.debug("this block has default values ...");
                }
                if (((InputInterfaceBlock) data).hasUnits()) {
                    logger.debug("this block has units ...");
                }
                if (list.size() == 1) {
                    endBlockScan();
                    scan(replace);
                    break;
                }
                restartAt = 1;
                scan(replace);
                break;
            } else {
                InputInterfaceEntry entry = (InputInterfaceEntry) data;
                int type = entry.getEntryType();
                if (type == InputInterfaceEntry.PRIMITIVE) {
                    InputInterfacePrimitiveEntry primitive = (InputInterfacePrimitiveEntry) entry;
                    logger.debug("    token: ");
                    logger.debug("        name: " + primitive.getName());
                    logger.debug("        value: " + primitive.getValue());
                    logger.debug("        unit: " + primitive.getUnit());
                } else if (type == InputInterfaceEntry.TABLE) {
                    InputInterfaceTable table = (InputInterfaceTable) entry;
                    String[] tableIdentifiers = table.getTableIdentifiers();
                    Vector tableData = table.getTableData();
                    if (replace && list.hasDefaults()) {
                        tableIdentifiers = getUniqueIdentifiers(defaultName,
                                tableIdentifiers);
                        table.setTableIdentifiers(tableIdentifiers);
                        expandTableData(tableData, tableIdentifiers.length);
                        Vector replaceVector = new Vector();
                        for (int j = 0; j < tableIdentifiers.length; j++) {
                            for (int jj = 0; jj < defaultName.length; jj++) {
                                if (tableIdentifiers[j].trim()
                                        .equalsIgnoreCase(
                                                defaultName[jj].trim())) {
                                    replaceVector.addElement(new String[] {
                                            Integer.toString(j),
                                            defaultValue[jj] });
                                }
                            }
                        }
                        for (int ii = 0; ii < tableData.size(); ii++) {
                            String[] cols = (String[]) tableData.get(ii);
                            for (int j = 0; j < replaceVector.size(); j++) {
                                String[] rep = (String[]) replaceVector.get(j);
                                int irep = Integer.parseInt(rep[0]);
                                if (cols[irep].trim().equals("*")) {
                                    cols[irep] = rep[1];
                                }
                            }
                        }
                    }
                    logger.debug("    table: ");
                    logger.debug("        ident: ");
                    logger.debug("            ");
                    for (int ii = 0; ii < tableIdentifiers.length; ii++) {
                        logger.debug("    " + tableIdentifiers[ii]);
                    }
                    logger.debug("        value:");
                    for (int ii = 0; ii < tableData.size(); ii++) {
                        String[] cols = (String[]) tableData.get(ii);
                        for (int j = 0; j < cols.length; j++) {
                            logger.debug("    " + cols[j]);
                        }
                    }

                    if (manager != null) {
                        logger.debug("obtaining table spec...");
                        InputInterfaceTableSpec tspec = manager
                                .getTableSpec(list.getFullName());
                        if (tspec != null) {
                            logger.debug("obtaining columns");
                            InputInterfaceTableColumns[] cols = tspec
                                    .getTableColumns();
                            if (cols != null) {
                                logger.debug("adding columns...");
                                ((InputInterfaceTable) entry)
                                        .setInputInterfaceTableColumns(cols);
                            }
                        }
                    }
                } else if (type == InputInterfaceEntry.DEFAULTS) {
                    InputInterfaceDefaults defs = (InputInterfaceDefaults) entry;
                    logger.debug("    defaults:");
                    String[] de = defs.getDefaults();
                    if (replace) {
                        defaultName = new String[de.length];
                        defaultValue = new String[de.length];
                    }
                    for (int ii = 0; ii < de.length; ii++) {
                        logger.debug("            default no." + ii + ": "
                                + de[ii]);
                        if (replace) {
                            String[] tmp = de[ii].split("=");
                            if (tmp[0] != null) {
                                defaultName[ii] = tmp[0].trim();
                            } else {
                                defaultName[ii] = "";
                            }
                            if (tmp[1] != null) {
                                defaultValue[ii] = tmp[1].trim();
                            } else {
                                defaultValue[ii] = "";
                            }
                        }
                    }
                } else if (type == InputInterfaceEntry.UNITS) {
                    InputInterfaceUnits units = (InputInterfaceUnits) entry;
                    logger.debug("    units:");
                    String[] un = units.getUnits();
                    for (int ii = 0; ii < un.length; ii++) {
                        logger.debug("            unit no." + ii + ": "
                                + un[ii]);
                    }
                }

                entryChanged(list.getFullName(), entry, null);

                if (i == list.size() - 1) {
                    endBlockScan();
                    scan(replace);
                    break;
                }
            }
        }
    }

    private void endBlockScan() {
        logger.debug("exiting block: " + list.getName());
        if (selectParentBlock()) {
            logger.debug("re-entering block: " + list.getName());
        } else {
            return;
        }
        restartAt = ((Integer) restartAts.pop()).intValue();
        logger.debug("popped: " + restartAt);
        if (restartAt == list.size()) {
            endBlockScan();
        }
    }

    /**
     * ユニークな識別子を返す.
     * 
     * @param ident1
     *            この識別子の中でユニークなやつがident2の後ろに足しこまれる.
     * @param ident2
     *            元の識別子(順序が変わらない方)
     */
    private String[] getUniqueIdentifiers(String[] ident1, String[] ident2) {
        if (ident1 == null || ident1.length == 0) {
            return ident2;
        }
        Vector foo = new Vector();
        for (int i = 0; i < ident1.length; i++) {
            boolean dupli = false;
            for (int j = 0; j < ident2.length; j++) {
                if (ident1[i].trim().equalsIgnoreCase(ident2[j].trim())) {
                    dupli = true;
                    continue;
                }
            }
            if (!dupli) {
                foo.addElement(ident1[i]);
            }
        }
        String[] ret = new String[ident2.length + foo.size()];
        for (int i = 0; i < ident2.length; i++) {
            ret[i] = ident2[i];
            logger.debug("at getUniqueIdentifiers: i, ret " + i + " " + ret[i]);
        }
        for (int i = 0; i < foo.size(); i++) {
            ret[ident2.length + i] = (String) foo.elementAt(i);
            logger.debug("at getUniqueIdentifiers: i, ret "
                    + new Integer(i + ident2.length) + " "
                    + ret[ident2.length + i]);
        }

        return ret;
    }

    /**
     * tableDataの「列を広げる」
     * 
     * @param tableData
     *            元のテーブルデータ
     * @param newSize
     *            新しい列の大きさ (元より同じか小さい場合は何もしない).
     */
    private void expandTableData(Vector tableData, int newSize) {
        if (tableData == null) {
            return;
        }

        for (int i = 0; i < tableData.size(); i++) {
            String[] tmp = (String[]) tableData.elementAt(i);
            if (tmp.length >= newSize) {
                return;
            }
            String[] newCol = new String[newSize];
            for (int j = 0; j < tmp.length; j++) {
                newCol[j] = tmp[j];
            }
            for (int j = tmp.length; j < newSize; j++) {
                newCol[j] = "*";
            }
            tableData.set(i, newCol);
        }
    }

    protected void replaceAst() {
        this.selectRoot();
        restartAts = new Stack();
        restartAt = 1;
        logger.debug("scanning list & replacing * with defaults ...");
        InputInterfaceBlock rootBlock = getRootList();
        for (int i = 1; i < rootBlock.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) rootBlock.get(i);
            if (data.isBlock()) {
                this.scan((InputInterfaceBlock) data, true);
            }
        }

        // this.scan(true);
    }

    public void setInputInterfaceTableSpecManager(
            InputInterfaceTableSpecManager manager) {
        this.manager = manager;
    }

    public InputInterfaceTableSpecManager getInputInterfaceTableSpecManager() {
        return this.manager;
    }

    public void setInputInterfaceTableSpecs(InputInterfaceTableSpec[] specs) {
        this.specs = specs;
    }

    public InputInterfaceTableSpec[] getInputInterfaceTableSpecs() {
        return specs;
    }

    private boolean writeDelimitedByColon = false;

    /**
     * デフォルトでは":"で区切られた値は一番最初のものだけが書き出される. このパラメターを trueにしておくと,
     * ":"でくぎられていようがいまいが全部書き出す.
     * 
     * @param writeDelimitedByColon
     *            上記の通り.
     */
    public void writeValueDelimitedByColon(boolean writeDelimitedByColon) {
        this.writeDelimitedByColon = writeDelimitedByColon;
    }

    /**
     * たとえ値が空白であったとしても書き出し時にその値を書き出したいか否かを制御する.
     * 
     * @param writeNullValue
     *            値が空白などでも書き出したい場合true.
     */
    public void writeNullValue(boolean writeNullValue) {
        this.writeNullValue = writeNullValue;
    }

    protected boolean iSavedIt = false;

    /**
     * 指定の名前のブロックを, 別のInputInterfaceオブジェクトの内容で"上書き"するメソッド. 既存のエントリーがある場合は上書きされ,
     * 無い場合は新規に作成され, 元々あるけどコピー元に無い情報はそのまま.
     * 
     * @param blockName
     *            この名前のブロックの下のブロックを上書きする
     * @param tagName
     *            上書きしたいブロックの名前
     * @param from
     *            このInputInterfaceオブジェクトに書いてある情報を利用する
     */
    public void overWriteBlock(String blockName, String tagName,
            InputInterface from) {
        from.selectBlock(blockName);
        InputInterfaceBlock block = from.getBlock(tagName);
        if (block == null) {
            logger.error("could not find block: " + blockName);
            return;
        }
        logger.debug("got block: " + block);
        selectAndCreateBlock(tagName);
        for (int i = 1; i < block.size(); i++) {
            InputInterfaceData data = (InputInterfaceData) block.get(i);
            if (data.isBlock()) {
                overWriteBlock(block.getName(), data.getName(), from);
                from.selectParentBlock();
                selectParentBlock();
            } else {
                replaceEntry((InputInterfaceEntry) data);
            }
        }
    }

    /**
     * 指定の名前のブロックを差し替えるメソッド.
     * 
     * @param blockName
     *            この名前のブロックを差し替える
     * @param tagName
     *            タグの名前
     * @param from
     *            このInputInterfaceオブジェクトに書いてある情報を利用する
     */
    public void swapBlock(String blockName, String tagName, InputInterface from) {
        from.selectBlock(blockName);
        InputInterfaceBlock block = from.getBlock(tagName);
        if (block == null) {
            logger.error("could not find block: " + blockName);
            return;
        }
        logger.debug("got block: " + block);

        selectAndCreateBlock(blockName);
        replaceBlock(block);
        selectRoot();
    }

    /**
     * 指定の名前のブロックを差し替えるメソッド. InputInterfaceEntryChangeEventを発行する場合はこちらを使う.
     * 
     * @param blockName
     *            この名前のブロックを差し替える
     * @param tagName
     *            タグの名前
     * @param from
     *            このInputInterfaceオブジェクトに書いてある情報を利用する
     * @param caller
     *            このメソッドをコールしたオブジェクト.
     */
    public void swapBlock(String blockName, String tagName,
            InputInterface from, InputInterfaceBlockChangeListener caller) {
        from.selectBlock(blockName);
        InputInterfaceBlock block = from.getBlock(tagName);
        if (block == null) {
            logger.error("could not find block: " + blockName);
            return;
        }
        logger.debug("got block: " + block);

        selectAndCreateBlock(blockName);
        replaceBlock(block, caller);
        selectRoot();
    }

    /** テスト用メイン */
    public static void main(String[] args) {
        InputInterface input = new InputInterface();
        InputInterfaceBlock block = new InputInterfaceBlock();
        block.setName("control");
        input.addBlock(block);
        input.selectBlock("control", null);

        InputInterfacePrimitiveEntry entry = new InputInterfacePrimitiveEntry();
        entry.setName("foo");
        entry.setValue("bar");
        entry.setUnit("hartree");
        input.addEntry(entry);

        input.selectRoot();
        input.scanList();
    }

    public String getType() {
        return "PHASE input object";
    }

    public static boolean isTrue(String str) {
        if (str == null || str.trim().length() == 0)
            return false;
        String st = str.trim();
        return st.equalsIgnoreCase("1") || st.equalsIgnoreCase("on")
                || st.equalsIgnoreCase("yes");
    }

}
