/*
 * GeneralComparator 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;

import java.util.Comparator;

/**
 * 一般的なオブジェクト比較クラス。
 * <br>
 * {@link java.util.Collections#sort(java.util.List) Collections#sort(List)}
 * メソッドを利用する際、引数のリストの要素が{@link java.util.Comparator
 * Comparable}を継承していないといけない（そうでない場合はメソッド内で
 * {@link java.lang.ClassCastException ClassCastException} がスローされる。
 * JDK 1.5以降では、リストの要素型に {@link java.lang.Comparable Comparable}
 * を継承したクラスを指定しないと、コンパイル・エラーになる）。
 * <br>
 * これに対して、
 * {@link java.util.Collections#sort(java.util.List, java.util.Comparator)
 * Collections#sort(List,Comparator)}メソッドは、引数の
 * {@link java.util.Comparator Comparator}オブジェクトが判定可能であれば、
 * リストの要素が{@link java.lang.Comparable Comparable}を継承していなくても
 * 問題なく処理される。
 * <br>
 * このクラスは、
 * {@link java.lang.Comparable Comparable}を継承していない要素を含むリスト
 * でもソートに使えるような一般的な {@link java.util.Comparator Comparator}
 * クラスである。
 * リストの要素が{@link java.lang.Comparable Comparable}を継承している場合は
 * {@link java.lang.Comparable#compareTo(java.lang.Object) Comparable#compareTo}
 * メソッドを使って比較し、
 * {@link java.lang.Comparable Comparable}を継承していない場合や
 * {@link java.lang.Comparable#compareTo(java.lang.Object) Comparable#compareTo}
 * メソッドで例外（{@link java.lang.ClassCastException ClassCastException}等)）
 * がスローされた場合は、{@link java.lang.Object#toString() toString()}メソッド
 * を使って両オブジェクトを文字列化した値を比較する。
 * 両オブジェクトを文字列化した値を比較して同じと判定された場合は、さらに
 * 両オブジェクトのクラス名を比較する。
 *
 * @author  佐藤隆之.
 * @version $Revision: 1.1.1.1 $, $Date: 2010-10-16 00:03:44 $
 */
public class GeneralComparator implements Comparator<Object>
{
  /**
   * デフォルト・コンストラクタ。
   */
  public GeneralComparator()
  {}

  /**
   * 指定された２つのオブジェクトの大小関係を比較する。
   * <br>
   * 一方のオブジェクトがヌルの場合は、もう一方がヌルでないならばヌルの方が
   * 大きい（昇順でソートしたとき後ろに来る）ものとして判定する。
   * <br>
   * 一方のオブジェクトが{@link java.lang.Comparable Comparable}を継承している
   * 場合は、その{@link java.lang.Comparable#compareTo(java.lang.Object)
   * Comparable#compareTo}メソッドを使って比較する。
   * <br>
   * どちらも{@link java.lang.Comparable Comparable}を継承していない場合や、
   * {@link java.lang.Comparable#compareTo(java.lang.Object)
   * Comparable#compareTo}メソッドで例外（{@link java.lang.ClassCastException
   * ClassCastException}等)）がスローされた場合は、両方のオブジェクトを
   * {@link java.lang.Object#toString() toString()}メソッドで文字列化して比較
   * する。
   * 両オブジェクトを文字列化した値を比較して同じと判定された場合は、さらに
   * 両オブジェクトのクラス名を比較する。
   *
   * @param  o1  比較対象のオブジェクト1。
   * @param  o2  比較対象のオブジェクト2。
   * @return o1がo2より小さい場合は正の値を、o1がo2より大きい場合は負の値を、
   *         o1とo2が等しい場合はゼロを返す。
   */
  public int compare(Object o1, Object o2)
  {
    if (o1 == null) {
      return (o2 == null) ? 0 : 1;
    }
    else if (o2 == null) {
      return -1;
    }

    try {
      return _cast_to_comparator(o1).compareTo(o2);
    }
    catch (Exception e) {
      try {
        return _cast_to_comparator(o2).compareTo(o1);
      }
      catch (Exception e2) {
        int n = (o1.toString().compareTo(o2.toString()));
        if (n != 0) {
          return n;
        }
        return o1.getClass().toString().compareTo(o2.getClass().toString());
      }
    }
  }

  @SuppressWarnings("unchecked")
  private Comparable<Object> _cast_to_comparator(Object o)
  {
    return (Comparable<Object>) o;
  }

  /**
   * 引数のオブジェクトがこのオブジェクトに等しいかどうかを判定する。
   *
   * @param  obj 比較対象のオブジェクト。 
   * @return 等しい場合は<tt>true</tt>を、そうでない場合は<tt>false</tt>を返す。
   */
  public boolean equals(Object obj)
  {
    return super.equals(obj);
  }
}

