scala.xml.Equality.scala Maven / Gradle / Ivy
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala
package xml
import scala.collection.Seq
/**
* In an attempt to contain the damage being inflicted on consistency by the
* ad hoc `equals` methods spread around `xml`, the logic is centralized and
* all the `xml` classes go through the `xml.Equality trait`. There are two
* forms of `xml` comparison.
*
* 1. `'''def''' strict_==(other: scala.xml.Equality)`
*
* This one tries to honor the little things like symmetry and hashCode
* contracts. The `equals` method routes all comparisons through this.
*
* 1. `xml_==(other: Any)`
*
* This one picks up where `strict_==` leaves off. It might declare any two
* things equal.
*
* As things stood, the logic not only made a mockery of the collections
* equals contract, but also laid waste to that of case classes.
*
* Among the obstacles to sanity are/were:
*
* Node extends NodeSeq extends Seq[Node]
* MetaData extends Iterable[MetaData]
* The hacky "Group" xml node which throws exceptions
* with wild abandon, so don't get too close
* Rampant asymmetry and impossible hashCodes
* Most classes claiming to be equal to "String" if
* some specific stringification of it was the same.
* String was never going to return the favor.
*/
object Equality {
def asRef(x: Any): AnyRef = x.asInstanceOf[AnyRef]
/**
* Note - these functions assume strict equality has already failed.
*/
def compareBlithely(x1: AnyRef, x2: String): Boolean = x1 match {
case x: Atom[?] => x.data == x2
case x: NodeSeq => x.text == x2
case _ => false
}
def compareBlithely(x1: AnyRef, x2: Node): Boolean = x1 match {
case x: NodeSeq if x.length == 1 => x2 == x(0)
case _ => false
}
def compareBlithely(x1: AnyRef, x2: AnyRef): Boolean =
if (x1 == null || x2 == null) x1 == null && x2 == null else x2 match {
case s: String => compareBlithely(x1, s)
case n: Node => compareBlithely(x1, n)
case _ => false
}
}
trait Equality extends scala.Equals {
protected def basisForHashCode: Seq[Any]
def strict_==(other: Equality): Boolean
def strict_!=(other: Equality): Boolean = !strict_==(other)
/**
* We insist we're only equal to other `xml.Equality` implementors,
* which heads off a lot of inconsistency up front.
*/
override def canEqual(other: Any): Boolean = other match {
case _: Equality => true
case _ => false
}
/**
* It's be nice to make these final, but there are probably
* people out there subclassing the XML types, especially when
* it comes to equals. However WE at least can pretend they
* are final since clearly individual classes cannot be trusted
* to maintain a semblance of order.
*/
override def hashCode: Int = basisForHashCode.##
override def equals(other: Any): Boolean = doComparison(other, blithe = false)
final def xml_==(other: Any): Boolean = doComparison(other, blithe = true)
final def xml_!=(other: Any): Boolean = !xml_==(other)
/**
* The "blithe" parameter expresses the caller's unconcerned attitude
* regarding the usual constraints on equals. The method is thereby
* given carte blanche to declare any two things equal.
*/
private def doComparison(other: Any, blithe: Boolean): Boolean = {
val strictlyEqual: Boolean = other match {
case x: AnyRef if this.eq(x) => true
case x: Equality => x.canEqual(this) && this.strict_==(x)
case _ => false
}
strictlyEqual || (blithe && Equality.compareBlithely(this, Equality.asRef(other)))
}
}