/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2003-2009, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

// $Id: $


package scala.util

/** <p>
 *    A convenience trait for simplifying hashCode creation.
 *    Mix this into a class and define <code>val hashValues = Seq(x1, x2, ...)</code>
 *    and your <code>hashCode</code> will be derived from those values.
 *    If you define <code>equals</code> in terms of <code>equalHashValues</code>
 *    then your <code>hashCode</code> and <code>equals</code> methods will
 *    never be out of sync.  Something like:
 *  </p><pre>
 *    <b>override def</b> equals(other: Any) = other <b>match</b> {
 *      <b>case</b> x: YourClass => <b>this</b> equalHashValues x
 *      <b>case</b> _            => <b>false</b>
 *    }</pre>
 *
 * @author Paul Phillips
 */
abstract trait Hashable extends AnyRef
{
  import Hashable._
  protected def hashValues: Seq[Any]  // in an ideal universe this would be more like Seq[Hashable]
  protected def hashSeed: Int = 1
  
  override def hashCode: Int = 
    (hashValues map calculateHashCode).foldLeft(hashSeed)((x, y) => x * 41 + y)
  
  protected def equalHashValues(other: Any) = other match {
    case x: Hashable  => hashValues sameElements x.hashValues
    case _            => false
  }
}
abstract trait StrictHashable extends Hashable
{
  protected def hashValues: Seq[Hashable]
}

object Hashable
{
  /** This implicit is for StrictHashable's benefit, so your hashValues Seq
   *  can contain both explicitly Hashable classes and value types.
   */
  implicit def anyVal2Hashable(x: AnyVal): Hashable =
    new Hashable { protected def hashValues = Seq(x) }

  private def calculateHashCode(x: Any) = x match {
    case null       => 0
    case x: AnyRef  => x.hashCode
    case x          => x.asInstanceOf[AnyRef].hashCode
  }
}