/*
 * MapListTable 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.util.Collection;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Collections;

/**
 * マップのリストとして実装されたテーブル・クラス。
 * <br>
 * レコードを表すマップに{@link java.util.HashMap HashMap}オブジェクトを使用
 * する。
 * 但し、カラム・キーの集合は{@link ts.util.table.Header Header}オブジェクト
 * によってレコード間で共用する。
 * <br>
 * レコード・コレクションはリストで実装されているため、レコードの順番があり、
 * カラムの値が同じレコードを複数格納することができる。
 * <br>
 * このテーブルは、各レコードのカラム構成が統一されていなかったり、
 * レコードによってカラムの値がところどころ抜けているような場合の利用に適して
 * いる。
 *
 * @param <C> カラム・キーのタイプ。
 * @param <V> カラムの値のタイプ。 
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.2 $, $Date: 2011-07-04 15:37:09 $
 */
public class MapListTable<C,V> extends AbstractTable<C,V>
{
  /** シリアル・バージョン番号。 */
  static final long serialVersionUID = -2802871239990289562L;

  /**
   * デフォルトコンストラクタ。
   */
  public MapListTable()
  {}

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

  /**
   * ヘッダを引数にとるコンストラクタ。
   *
   * @param  header ヘッダ・オブジェクト。
   * @throws AssertionError 引数がヌルの場合（デバッグ・モードのみ）。
   */
  public MapListTable(ts.util.table.Header<C> header)
  {
    super(header);
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数が負の値の場合（デバッグ・モードのみ）。
   */
  @Override
  protected ts.util.table.Header<C> createHeader(int initColCapacity)
  {
    return new Header<C>(initColCapacity);
  }

  /**
   * {@inheritDoc}
   *
   * @throws AssertionError 引数が負の値の場合（デバッグ・モードのみ）。
   */
  @Override
  protected Collection<Record<C,V>> createRecordCollection(int initRecCapacity)
  {
    assert (initRecCapacity >= 0) : "@param:initRecCapacity is negative.";
    return new LinkedList<Record<C,V>>();
  }

  /**
   * レコード・オブジェクトを作成する。
   *
   * @param  initColCapacity 初期カラム容量。
   * @return レコード・オブジェクト。
   * @throws AssertionError 引数が負の値の場合（デバッグ・モードのみ）。
   */
  @Override
  protected Record<C,V> createRecord(int initColCapacity)
  {
    assert (initColCapacity >= 0) : "@param:initColCapacity is negative.";
    return new MapListTableRecord();
  }

  /* -- inner class -- */

  /**
   * {@link ts.util.table.MapListTable MapListTable}クラスが使用する
   * ヘッダ・クラス。
   */
  public static class Header<C> implements ts.util.table.Header<C>
  {
    /** シリアル・バージョン番号。 */
    static final long serialVersionUID = 7045899894614584582L;

    /** カラム・キーを格納する一意集合オブジェクト。 */
    private Set<C> columnSet_ ;

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

    /**
     * カラム数を取得する。
     *
     * @return カラム数。
     */
    public int columnCount()
    {
      return columnSet_.size();
    }

    /**
     * カラム・キーのイテレータを取得する。
     *
     * @return カラム・キーのイテレータ。
     */
    public Enumeration<C> columns()
    {
      return Collections.enumeration(columnSet_);
    }

    /**
     * このヘッダに指定されたカラム・キーが格納されているかどうかを確認する。
     *
     * @param  column カラム・キー。
     * @return 引数のカラム・キーが、このヘッダに格納されている場合は
     *         <tt>true</tt>を返す。
     */
    public boolean hasColumn(Object column)
    {
      return columnSet_.contains(column);
    }

    /**
     * カラム・キーを追加する。
     *
     * @param  column カラム・キー。
     */
    public void addColumn(C column)
    {
      columnSet_.add(column);
    }
  }

  /**
   * {@link ts.util.table.MapListTable MapListTable}で使用するレコード・クラス。
   */
  protected class MapListTableRecord extends Record<C,V>
  {
    /** シリアル・バージョン番号。 */
    static final long serialVersionUID = 1964738182974536709L;

    /** 値を格納するマップ。 */
    private Map<C,V> valueMap_ = new HashMap<C,V>();

    /**
     * デフォルト・コンストラクタ。
     */
    public MapListTableRecord()
    {}

    /**
     * {@inheritDoc}
     */
    @Override
    protected ts.util.table.Header<C> header()
    {
      return MapListTable.this.header();
    }

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

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

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

