/*
 * AbstractTable class.
 *
 * Copyright (C) 2007 SATOH Takayuki All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package ts.util.table;

import java.io.Serializable;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Collections;

/**
 * {@link ts.util.table.Table Table}インターフェイスをインプリメントするクラス
 * のベースとなる抽象クラス。
 * <br>
 * テーブルを、{@link ts.util.table.Header Header}オブジェクトと、レコードを
 * 表すマップのコレクションによって構成されるものとして、それに基づいた
 * 各メソッドの実装を提供する。
 *
 * @param <C> このテーブルのカラム・キーのタイプ。
 * @param <V> このテーブルのカラム値のタイプ。
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.5 $, $Date: 2011-10-29 14:56:23 $
 */
public abstract class AbstractTable<C,V> implements Table<C,V>, Cloneable
{
  /** シリアル・バージョン番号。 */
  static final long serialVersionUID = 1469325734478989265L;

  /** ヘッダ・オブジェクト。　*/
  private Header<C> header_ ;

  /** レコードを格納するコレクション。 */
  private Collection<Record<C,V>> recordCollection_ ;

  /** インデックスを格納するマップ。 */
  private Map<LinkedHashSet<C>, Index<C,V>> indexMap_ =
    new HashMap<LinkedHashSet<C>,Index<C,V>>();

  /** トリガを格納するリスト。*/
  private List<Trigger<C,V>> triggerLst_ = new LinkedList<Trigger<C,V>>();

  /**
   * デフォルトコンストラクタ。
   */
  public AbstractTable()
  {
    this(16, 0);
  }

  /**
   * 初期レコード容量と初期カラム容量を引数にとるコンストラクタ。
   *
   * @param  initRecCapacity 初期レコード容量。
   * @param  initColCapacity 初期カラム容量。
   * @throws AssertionError 引数が負の値の場合（デバッグ・モードのみ）。
   */
  public AbstractTable(int initRecCapacity, int initColCapacity)
  {
    assert (initRecCapacity >= 0) : "@param:initRecCapacity is negative.";
    assert (initColCapacity >= 0) : "@param:initColCapacity is negative.";

    header_ = createHeader(initColCapacity);
    recordCollection_ = createRecordCollection(
      (initRecCapacity < 0) ? 0 : initRecCapacity);
  }

  /**
   * ヘッダを引数にとるコンストラクタ。
   *
   * @param  header ヘッダ・オブジェクト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public AbstractTable(Header<C> header)
  {
    assert (header != null) : "@param:header is null.";
    header_ = header;
    recordCollection_ = createRecordCollection(10);
  }

  /**
   * テーブルを構成するヘッダを取得する。
   *
   * @return ヘッダ・オブジェクト。
   */
  protected Header<C> header()
  {
    return header_ ;
  }

  /**
   * テーブルを構成するカラムの数を取得する。
   *
   * @return カラム数。
   */
  public int columnCount()
  {
    return header_.columnCount();
  }

  /**
   * テーブルに格納されているレコードの数を取得する。
   *
   * @return レコード数。
   */
  public int recordCount()
  {
    return recordCollection_.size();
  }

  /**
   * このテーブルを構成するカラム・キーを列挙する。
   *
   * @return カラム・キーの列挙オブジェクト。
   */
  public Enumeration<C> columns()
  {
    return header_.columns();
  }

  /**
   * このテーブルに格納されているレコードを順に取り出すためのイテレータを取得
   * する。
   *
   * @return レコードのイテレータ。
   */
  public MapIterator<C,V> records()
  {
    return new MapIterator<C,V>() {
      private Iterator<Record<C,V>> it_ = recordCollection_.iterator();
      public boolean hasNext() { return it_.hasNext(); }
      public Map<C,V> next() { return it_.next(); }
      public void remove() { it_.remove(); }
    };
  }

  /**
   * このテーブルに格納されているレコードをソートして、順に取り出すための
   * イテレータを取得する。
   *
   * @param  comparator ソートに使用するレコード比較オブジェクト。
   * @return レコードのイテレータ。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public MapIterator<C,V> records(MapComparator<C,V> comparator)
  {
    assert (comparator != null) : "@param:comparator is null.";

    final List<Map<C,V>> recL = new LinkedList<Map<C,V>>(recordCollection_);

    Collections.sort(recL, comparator);

    return new MapIterator<C,V>() {
      private Iterator<Map<C,V>> it_ = recL.iterator();
      private Object rec_ = this;
      public boolean hasNext() { return it_.hasNext(); }
      public Map<C,V> next() { Map<C,V> r = it_.next(); rec_ = r; return r; }
      public void remove() { it_.remove(); recordCollection_.remove(rec_); }
    };
  }

  /**
   * {@inheritDoc}
   */
  public Map<C,V> recordFirst()
  {
    if (recordCollection_.size() > 0) {
      return recordCollection_.iterator().next();
    }
    else {
      return null;
    }
  }

  /**
   * {@inheritDoc}
   */
  public Map<C,V> appendNew()
  {
    return appendNew(header_.columnCount());
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数が負の値の場合（デバッグ・モードのみ）。
   */
  public Map<C,V> appendNew(int initColCapacity)
  {
    assert (initColCapacity >= 0) : "@param:initColCapacity is negative.";

    Record<C,V> rec = new IndexAndTriggerSupportRecord(
      createRecord(initColCapacity));

    List<Trigger<C,V>> trgL = null;
    if (triggerLst_.size() > 0) {
      trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
      for (Trigger<C,V> trigger : triggerLst_) {
        Trigger<C,V> trg = trigger.createClone();
        trg.preAppend(this, rec);
        trgL.add(trg);
      }
    }

    recordCollection_.add(rec);

    if (trgL != null) {
      for (Trigger<C,V> trg : trgL) {
        trg.postAppend(this, rec);
      }
    }

    for (Index<C,V> ind : indexMap_.values()) {
      ind.getIndexedCollectionByForce(rec).add(rec);
    }

    return rec;
  }

  /**
   * このテーブルに格納されているレコードを全て削除する。
   */
  public void deleteAll()
  {
    Collection<Map<C,V>> c = Collections.unmodifiableCollection(
      (Collection<? extends Map<C,V>>) recordCollection_);

    List<Trigger<C,V>> trgL = null;
    if (triggerLst_.size() > 0) {
      trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
      for (Trigger<C,V> trigger : triggerLst_) {
        Trigger<C,V> trg = trigger.createClone();
        trg.preDeleteAll(this, c);
        trgL.add(trg);
      }
    }

    recordCollection_ = createRecordCollection(16);

    if (trgL != null) {
      for (Trigger<C,V> trg : trgL) {
        trg.postDeleteAll(this, c);
      }
    }

    for (Index<C,V> ind : indexMap_.values()) {
      ind.deleteAllRecordsFromIndex();
    }
  }

  /**
   * {@inheritDoc}
   */
  public boolean exists(C column, V value)
  {
    Map<C,V> condition = new HashMap<C,V>();
    condition.put(column, value);
    return exists(condition);
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public boolean exists(Map<C,V> condition)
  {
    assert (condition != null) : "@param:condition is null.";
    return existsInCollection(recordCollection_, condition);
  }

  /**
   * 指定されたレコード・コレクションの要素に、条件に該当するレコードが存在する
   * かどうかを確認する。
   *
   * @param coll レコード・コレクション。
   * @param condition 条件のカラム・キーとその値を格納したマップ・オブジェクト。
   * @return 該当するレコードが存在する場合は<tt>true</tt>を返す。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  protected boolean existsInCollection(
    Collection<Record<C,V>> coll, Map<C,V> condition)
  {
    assert (coll != null) : "@param:coll is null.";
    assert (condition != null) : "@param:condition is null.";

    for (Record<C,V> rec : coll) {
      if (rec.satisfyCondition(condition)) {
        return true;
      }
    }
    return false;
  }

  /**
   * {@inheritDoc}
   */
  public Map<C,V> selectFirst(C column, V value)
  {
    Map<C,V> condition = new HashMap<C,V>();
    condition.put(column, value);
    return selectFirst(condition);
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public Map<C,V> selectFirst(Map<C,V> condition)
  {
    assert (condition != null) : "@param:condition is null.";
    return selectFirstFromCollection(recordCollection_, condition);
  }

  /**
   * 指定されたレコード・コレクションの要素の中から、条件に最初に該当した
   * レコードを取得する。
   *
   * @param coll レコード・コレクション。
   * @param condition 条件のカラム・キーとその値を格納したマップ・オブジェクト。
   * @return 引数のマップに格納された条件に最初に該当したレコード。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  protected Map<C,V> selectFirstFromCollection(
    Collection<Record<C,V>> coll, Map<C,V> condition)
  {
    assert (coll != null) : "@param:coll is null.";
    assert (condition != null) : "@param:condition is null.";

    for (Record<C,V> rec : coll) {
      if (rec.satisfyCondition(condition)) {
        return rec;
      }
    }
    return null;
  }

  /**
   * {@inheritDoc}
   */
  public List<Map<C,V>> select(C column, V value)
  {
    Map<C,V> condition = new HashMap<C,V>();
    condition.put(column, value);
    return select(condition);
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public List<Map<C,V>> select(Map<C,V> condition)
  {
    assert (condition != null) : "@param:condition is null.";
    return selectFromCollection(recordCollection_, condition);
  }

  /**
   * 指定されたコレクションの要素の中から、条件に該当するレコードを取り出して
   * リストに格納して返す。
   *
   * @param coll レコード・コレクション。
   * @param condition 条件のカラム・キーとその値を格納したマップ・オブジェクト。
   * @return 引数のマップに格納された条件に該当するレコードを格納したリスト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  protected List<Map<C,V>> selectFromCollection(
    Collection<Record<C,V>> coll, Map<C,V> condition)
  {
    assert (coll!= null) : "@param:coll is null.";
    assert (condition != null) : "@param:condition is null.";

    List<Map<C,V>> resultL = new LinkedList<Map<C,V>>();

    for (Record<C,V> rec : coll) {
      if (rec.satisfyCondition(condition)) {
        resultL.add(rec);
      }
    }
    return resultL;
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public List<Map<C,V>> delete(Map<C,V> condition)
  {
    assert (condition != null) : "@param:condition is null.";
    return deleteFromCollection(recordCollection_, condition);
  } 

  /**
   * 指定されたコレクションの中から、条件に該当するレコードを削除する。
   *
   * @param coll レコード・コレクション。
   * @param condition 条件のカラム・キーとその値を格納したマップ・オブジェクト。
   * @return 削除されたレコードを格納したリスト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  protected List<Map<C,V>> deleteFromCollection(
    Collection<Record<C,V>> coll, Map<C,V> condition)
  {
    assert (coll != null) : "@param:coll is null.";
    assert (condition != null) : "@param:condition is null.";

    List<Map<C,V>> resultL = new LinkedList<Map<C,V>>();

    Iterator<Record<C,V>> it = coll.iterator();
    while (it.hasNext()) {
      Record<C,V> rec = it.next();
      if (rec.satisfyCondition(condition)) {

        List<Trigger<C,V>> trgL = null;
        if (triggerLst_.size() > 0) {
          trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
          for (Trigger<C,V> trigger : triggerLst_) {
            Trigger<C,V> trg = trigger.createClone();
            trg.preDelete(this, rec);
            trgL.add(trg);
          }
        }

        if (coll == recordCollection_) {
          it.remove();
        }
        else {
          recordCollection_.remove(rec);
        }
        resultL.add(rec);

        if (trgL != null) {
          for (Trigger<C,V> trg : trgL) {
            trg.postDelete(this, rec);
          }
        }

        for (Index<C,V> ind : indexMap_.values()) {
          ind.getIndexedCollection(rec).remove(rec);
        }
      }
    }

    return resultL;
  }

  /**
   * {@inheritDoc}
   */
  public List<Map<C,V>> update(Map<C,V> condition, Map<C,V> destination)
  {
    assert (condition != null) : "@param:condition is null.";
    assert (destination != null) : "@param:destination is null.";

    return updateInCollection(recordCollection_, condition, destination);
  }

  /**
   * 指定されたコレクションの要素の中から、条件に該当するレコードを取り出して
   * 内容を更新する。
   * <br>
   * 更新したレコードは、リストに格納して返す。
   *
   * @param coll レコード・コレクション。
   * @param condition 条件のカラム・キーとその値を格納したマップ・オブジェクト。
   * @param destination 更新するカラム・キーとその値を格納した
   *          マップ・オブジェクト。
   * @return 削除されたレコードを格納したリスト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  protected List<Map<C,V>> updateInCollection(
    Collection<Record<C,V>> coll, Map<C,V> condition, Map<C,V> destination)
  {
    assert (coll != null) : "@param:coll is null.";
    assert (condition != null) : "@param:condition is null.";
    assert (destination != null) : "@param:destination is null.";

    List<Map<C,V>> resultL = new LinkedList<Map<C,V>>();

    for (Record<C,V> rec : coll) {
      if (rec.satisfyCondition(condition)) {
        rec.putAll(destination);
        resultL.add(rec);
      }
    }

    return resultL;
  }

  /**
   * 指定されたカラム・キーの配列をインデックス・キーとするインデックスを
   * 取得する。
   * <br>
   * 該当するインデックスが存在しない場合は、新しくインデックスを作成して
   * 返す。
   *
   * @param  indexKeys インデックス・キーの配列。
   * @return 指定されたインデックス・キーを持つインデックス・オブジェクト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   * @throws IllegalArgumentException 引数の配列が空の場合。
   */
  public Index<C,V> getIndex(C ... indexKeys)
  {
    assert (indexKeys != null) : "@param:indexKeys is null";
    
    if (indexKeys.length == 0) {
      throw new IllegalArgumentException("indexKeys is an empty array.");
    }

    LinkedHashSet<C> set = new LinkedHashSet<C>();
    for (C c : indexKeys) {
      set.add(c);
    }
    C[] arr = _cast_to_columns(set.toArray(new Object[set.size()]));

    Index<C,V> ind = indexMap_.get(set);
    if (ind == null) {
      ind = new AbstractTableIndex(arr);
      indexMap_.put(set, ind);
      for (Record<C,V> rec : recordCollection_) {
        ind.getIndexedCollectionByForce(rec).add(rec);
      }
    }

    return ind;
  }

  /**
   * このテーブルにトリガを追加する。
   *
   * @param  trigger トリガ・オブジェクト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public void addTrigger(Trigger<C,V> trigger)
  {
    assert (trigger != null) : "@param:trigger is null.";
    triggerLst_.add(trigger);
  }

  /**
   * カラム・キーを格納する{@link ts.util.table.Header Header}オブジェクトを
   * 作成する。
   *
   * @param  initColCapacity 初期カラム容量。
   * @return カラム・キーを格納する{@link ts.util.table.Header Header}
   *         オブジェクト。
   */
  protected abstract Header<C> createHeader(int initColCapacity);

  /**
   * レコードを表すマップを格納するコレクション・オブジェクトを作成する。
   *
   * @param  initRecCapacity 初期レコード容量。
   * @return レコードを表すマップを格納するコレクション・オブジェクト。
   */
  protected abstract Collection<Record<C,V>> createRecordCollection(
    int initRecCapacity);

  /**
   * レコード・オブジェクトを作成する。
   *
   * @param  initColCapacity 初期カラム容量。
   * @return レコード・オブジェクト。
   */
  protected abstract Record<C,V> createRecord(int initColCapacity);


  /* -- inner class -- */

  /**
   * {@link ts.util.table.AbstractTable AbstractTable}クラス用に
   * {@link ts.util.table.Record Record}オブジェクトをラップするクラス。
   * <br>
   * {@link ts.util.table.Record Record}オブジェクトの内容に変更があった場合に、
   * {@link ts.util.table.AbstractTable AbstractTable}オブジェクトに格納されて
   * いるインデックスの更新やトリガの実行を行う。
   */
  private final class IndexAndTriggerSupportRecord extends Record<C,V>
  {
    /** シリアル・バージョン番号。 */
    static final long serialVersionUID = 7787001267275530819L;

    /** ラップされるレコード・オブジェクト。 */
    private Record<C,V> record_ ;

    /**
     * レコード・オブジェクトを引数にとるコンストラクタ。
     *
     * @param  record レコード・オブジェクト。
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public IndexAndTriggerSupportRecord(Record<C,V> record)
    {
      assert (record != null) : "@param:record is null.";
      record_ = record;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Header<C> header()
    {
      return record_.header();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int size()
    {
      return record_.size();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEmpty()
    {
      return record_.isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean containsKey(Object column)
    {
      return record_.containsKey(column);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean containsValue(Object value)
    {
      return record_.containsValue(value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public V get(Object column)
    {
      return record_.get(column);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected V getValue(Object column)
    {
      return record_.getValue(column);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected V putValue(C column, V value)
    {
      return record_.putValue(column, value);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected V removeValue(Object column)
    {
      return record_.removeValue(column);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public V put(C column, V value)
    {
      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollection(this).remove(this);
      }

      List<Trigger<C,V>> trgL = null;
      Map<C,V> dest = null;
      if (triggerLst_.size() > 0) {
        dest = new HashMap<C,V>();
        dest.put(column, value);

        trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
        for (Trigger<C,V> trigger : triggerLst_) {
          Trigger<C,V> trg = trigger.createClone();
          trg.preUpdate(AbstractTable.this, this.record_, dest);
          trgL.add(trg);
        }
      }

      V ret;
      if (dest != null && dest.size() > 1) {
        ret = record_.get(column);
        record_.putAll(dest);
      }
      else {
        ret = record_.put(column, value);
      }

      if (trgL != null) {
        for (Trigger<C,V> trg : trgL) {
          trg.postUpdate(AbstractTable.this, this.record_, dest);
        }
      }

      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollectionByForce(this).add(this);
      }

      return ret;
    }

    /**
     * {@inheritDoc}
     */
    public V remove(Object column)
    {
      if (! record_.containsKey(column)) {
        return null;
      }

      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollection(this).remove(this);
      }

      V ret;
      if (triggerLst_.size() > 0) {
        C col = _cast_to_column(column);

        Map<C,V> dest = new HashMap<C,V>();
        dest.put(col, null);
    
        List<Trigger<C,V>> trgL;
        trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
        for (Trigger<C,V> trigger : triggerLst_) {
          Trigger<C,V> trg = trigger.createClone();
          trg.preUpdate(AbstractTable.this, this.record_, dest);
          trgL.add(trg);
        }

        ret = record_.remove(column);
        if (dest.size() > 1) {
          record_.putAll(dest);
        }

        for (Trigger<C,V> trg : trgL) {
          trg.postUpdate(AbstractTable.this, this.record_, dest);
        }
      }
      else {
        ret = record_.remove(column);
      }

      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollectionByForce(this).add(this);
      }

      return ret;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void putAll(Map<? extends C, ? extends V> m)
    {
      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollection(this).remove(this);
      }
      Map<C,V> dest = _cast_to_map(m);

      List<Trigger<C,V>> trgL = null;
      if (triggerLst_.size() > 0) {
        trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());
        for (Trigger<C,V> trigger : triggerLst_) {
          Trigger<C,V> trg = trigger.createClone();
          trg.preUpdate(AbstractTable.this, this.record_, dest);
          trgL.add(trg);
        }
      }

      record_.putAll(m);

      if (trgL != null) {
        for (Trigger<C,V> trg : trgL) {
          trg.postUpdate(AbstractTable.this, this.record_, dest);
        }
      }

      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollectionByForce(this).add(this);
      }
    }

    /**
     * {@inheritDoc} 
     */
    @Override
    public void clear()
    {
      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollection(this).remove(this);
      }

      if (triggerLst_.size() > 0) {

        @SuppressWarnings("serial")
        class _ClearMap extends Record<C,V>
        {
          class _MutableClearMapHeader implements Header<C>
          {
            private Set<C> set_ = new HashSet<C>();

            _MutableClearMapHeader(Header<C> hdr) {
              Enumeration<C> enm = hdr.columns();
              while (enm.hasMoreElements()) {
                set_.add(enm.nextElement());
              }
            }
            public int columnCount() {
              return set_.size();
            }
            public Enumeration<C> columns() { 
              return Collections.enumeration(set_);
            }
            public boolean hasColumn(Object c) {
              return set_.contains(c);
            }
            public void addColumn(C c) {
              set_.add(c);
            }
          }

          class _ClearMapHeader implements Header<C> 
          {
            private Header<C> base_ = AbstractTable.this.header();

            public int columnCount() {
              return base_.columnCount();
            }
            public Enumeration<C> columns() {
              return base_.columns();
            }
            public boolean hasColumn(Object c) {
              return base_.hasColumn(c);
            }
            public void addColumn(C c) {
              if (base_.hasColumn(c)) return;
              if (base_ == AbstractTable.this.header()) {
                base_ = new _MutableClearMapHeader(base_);
              }
              base_.addColumn(c);
            }
          }

          private Header<C> header_ = new _ClearMapHeader();
          private Map<C,V> map_ = new HashMap<C,V>();
          private boolean modified_ = false;

          @Override protected Header<C> header() {
            return header_;
          }
          @Override protected V getValue(Object c) {
            return map_.get(c);
          }
          @Override protected V putValue(C c, V v) {
            modified_ = true;
            return map_.put(c, v);
          }
          @Override protected V removeValue(Object c) {
            modified_ = true;
            return map_.remove(c);
          }
          boolean isModified() {
            return modified_ ;
          }
        }
        _ClearMap dest = new _ClearMap();

        List<Trigger<C,V>> trgL;
        trgL = new ArrayList<Trigger<C,V>>(triggerLst_.size());

        for (Trigger<C,V> trigger : triggerLst_) {
          Trigger<C,V> trg = trigger.createClone();
          trg.preUpdate(AbstractTable.this, this.record_, _cast_to_map(dest));
          trgL.add(trg);
        }

        record_.clear();
        if (dest.isModified()) {
          record_.putAll(_cast_to_map(dest));
        }

        for (Trigger<C,V> trg : trgL) {
          trg.postUpdate(AbstractTable.this, this.record_, _cast_to_map(dest));
        }
      }
      else {
        record_.clear();
      }

      for (Index<C,V> ind : indexMap_.values()) {
        ind.getIndexedCollectionByForce(this).add(this);
      }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<C> keySet()
    {
      return record_.keySet();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<V> values()
    {
      return record_.values();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Map.Entry<C,V>> entrySet()
    {
      return record_.entrySet();
    }
  }
  
  /**
   * {@link ts.util.table.AbstractTable AbstractTable}クラス用の
   * インデックス・クラス。
   */
  protected class AbstractTableIndex extends Index<C,V>
  {
    /** シリアル・バージョン番号。 */ 
    static final long serialVersionUID = 541381865416736128L;

    /**
     * インデックス・キーの配列を引数にとるコンストラクタ。
     *
     * @param  indexKeys インデックス・キーの配列。
     * @throws IllegalArgumentException 引数の配列のサイズがゼロの場合。
     * @throws AssertionError 引数がヌルの場合。
     */
    protected AbstractTableIndex(C[] indexKeys)
    {
      super(indexKeys);
    }

    /**
     * このインデックスの作成元であるテーブル・オブジェクトを取得する。
     *
     * @return 作成元のテーブル・オブジェクト。
     */
    @Override
    protected Table<C,V> getBaseTable()
    {
      return AbstractTable.this;
    }

    /**
     * {@inheritDoc}
     *
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public boolean exists(Map<C,V> condition)
    {
      return existsInCollection(collectRecords(condition), condition);
    }
  
    /**
     * {@inheritDoc}
     *
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public Map<C,V> selectFirst(Map<C,V> condition)
    {
      return selectFirstFromCollection(collectRecords(condition), condition);
    }
  
    /**
     * {@inheritDoc}
     *
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public List<Map<C,V>> select(Map<C,V> condition)
    {
      return selectFromCollection(collectRecords(condition), condition);
    }
  
    /**
     * {@inheritDoc}
     *
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public List<Map<C,V>> delete(Map<C,V> condition)
    {
      return deleteFromCollection(collectRecords(condition), condition);
    }
  
    /**
     * {@inheritDoc}
     *
     * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
     */
    public List<Map<C,V>> update(Map<C,V> condition, Map<C,V> destination)
    {
      return updateInCollection(collectRecords(condition), condition,
        destination);
    }
  }

  @SuppressWarnings("unchecked")
  private Map<C,V> _cast_to_map(Object o)
  {
    return (Map<C,V>) o;
  }

  @SuppressWarnings("unchecked")
  private C _cast_to_column(Object o)
  {
    return (C) o;
  }

  @SuppressWarnings("unchecked")
  private C[] _cast_to_columns(Object[] arr)
  {
    return (C[]) arr;
  }
}

