
org.opalj.fpcf.EOptionP.scala Maven / Gradle / Ivy
The newest version!
/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package fpcf
/**
* An entity associated with the current extension of a property or `None` if no (preliminary)
* property is already computed.
*
* @author Michael Eichberg
*/
sealed trait EOptionP[+E <: Entity, +P <: Property] {
/**
* The entity. E.g., a class, method or field. In general, it is recommended
* to use entities that stand for specific elements in the code, but which
* are not the concrete source code entities themselves. This greatly facilitates
* associating properties with entities where the respective code is not available.
* For example, by using an object which acts as a representative for a concrete method
* it is possible to associate (predetermined) properties with (selected) library methods
* even if those methods are not part of the analysis.
*
* @note Entities have to implement `equals`/`hashCode` methods which are very efficient,
* because entity based comparisons happen very frequently!
*/
val e: E
/**
* The property key of the optionally available property.
*/
def pk: PropertyKey[P]
def isEPK: Boolean
def asEPK: EPK[E, P]
/** This EOptionP as a pair of an entity and a property key. */
def toEPK: EPK[E, P]
/**
* @return `true` if the entity is associated with a (preliminary) property which represents
* an upper bound. Always `true` in case of a `FinalP`.
*/
def hasUBP: Boolean
final def hasNoUBP: Boolean = !hasUBP
/**
* @return `true` if the entity is associated with a (preliminary) property which represents
* a lower bound. Always `true` in case of a `FinalP`.
*/
def hasLBP: Boolean
final def hasNoLBP: Boolean = !hasLBP
def isEPS: Boolean
/**
* This EOptionP as an EPS object; defined iff at least a preliminary property exists.
*/
def asEPS: EPS[E, P]
def toEPS: Option[EPS[E, P]]
/**
* Returns `true` if and only if we have a property and the property was stored in the
* store using `(Multi)Result`.
*/
def isFinal: Boolean
final def isRefinable: Boolean = !isFinal
def asFinal: FinalEP[E, P]
def asInterim: InterimEP[E, P]
/**
* Combines the test if we have a final property and – if we have one – if it is equal (by
* means of an equality check) to the given one.
*/
def is(p: AnyRef): Boolean = /*this.hasProperty && */ this.isFinal && this.ub == p
/**
* Converts an `InterimEP` to a `FinalEP`; fails otherwise.
*/
private[fpcf] def toFinalEP: FinalEP[E, P]
private[fpcf] def toFinalEUBP: FinalEP[E, P]
private[fpcf] def toFinalELBP: FinalEP[E, P]
/**
* Returns the upper bound of the property if it is available – [[hasUBP]] has to be
* `true` – otherwise an `UnsupportedOperationException` is thrown.
*
* The upper bound always models the best/most precise result w.r.t. the underlying lattice.
* Here, "best" means that the set of potentially reachable states/instructions that the
* analyzed program can ever assume is potentially smaller when compared to a worse property.
*
* The upper bound models the sound and precise result under the assumption that the
* properties of all explicitly and implicitly relevant entities is as last queried or
* implicitly assumed. I.e., unless a dependee is updated the upper bound represents
* the correct and most precise result.
*
* The lower bound models the worst case property that a specific entity can have under the
* assumption that all other relevant properties will get their worst properties. This
* can – but does not have to be – the underlying lattice's bottom value.
* The lower bound is generally helpful for client analyses to determine final
* results quicker. For example, imagine the following code:
* {{{
* def f(a : AnyRef) : Unit = a match {
* case a : List[_] => if (a.exists( _ == null)) throw new IllegalArgumentException
* case _ => throw new UnknownError
* }
* def m(){
* try {
* f(List(1,2,3))
* } catch {
* case nfe: NumberFormatException => ...
* }
* }
* }}}
* In that case – assuming we do not perform context sensitive analyses –
* if the lower bound for `f` for the set of thrown exceptions is determined
* to be `Set(IllegalArgumentException,UnkownError)`, then the catch of the
* `NumberFormatException` can be ruled out and a final result for `m` can be
* computed.
*
* @note If the property is final, lb (and ub) will return the final property `p`.
*/
@throws[UnsupportedOperationException]("if no property is available")
def ub: P
/**
* Returns the lower bound of the property if it is available,
* otherwise an `UnsupportedOperationException` is thrown. For details regarding the
* precise semantics see the discussion for [[ub]].
*
* @note If the property is final, lb (and ub) will return the final property `p`.
*/
@throws[UnsupportedOperationException]("if no property is available")
def lb: P
/**
* Returns `true` if this `EOptionP` is updated when compared with the given `oldEOptionP`,
* provided that this `EOptionP` is guaranteed to be at most as old as `oldEOptionP`.
* That is, this EPS is considered to be newer if the properties are different.
*
* @note The caller has to ensure that this EOptionP and and the given EOptionP are
* comparable. That is, they define properties of the same kind associated with
* the same entity and same bounds.
*/
private[fpcf] def isUpdatedComparedTo(oldEOptionP: EOptionP[Entity, Property]): Boolean
@throws[IllegalArgumentException]("if the given eps is not a valid update")
private[fpcf] def checkIsValidPropertiesUpdate(
eps: SomeEPS,
newDependees: Iterable[SomeEOptionP]
): Unit
}
/**
* Factory and extractor for [[EOptionP]] objects.
*
* @author Michael Eichberg
*/
object EOptionP {
def unapply[E <: Entity, P <: Property](eOptP: EOptionP[E, P]): Some[(E, PropertyKey[P])] = {
Some((eOptP.e, eOptP.pk))
}
}
/**
* A pairing of an [[Entity]] and an associated [[Property]] along with its state.
*
* @note Entities and properties are compared using `equals`.
*
* @author Michael Eichberg
*/
sealed trait EPS[+E <: Entity, +P <: Property] extends EOptionP[E, P] {
final override def isEPK: Boolean = false
def asEPK: EPK[E, P] = throw new ClassCastException();
final override def toEPK: EPK[E, P] = EPK(e, pk)
/**
* Creates a [[FinalP]] object using the current ub if the ub is available. If the ub
* is not available an exception is thrown.
*
* @note No check is done whether the property is actually final.
*/
override def toFinalEUBP: FinalEP[E, P] = FinalEP(e, ub)
/**
* Creates a [[FinalP]] object using the current lb if the lb is available. If the lb
* is not available an exception is thrown.
*
* @note No check is done whether the property is actually final.
*/
override def toFinalELBP: FinalEP[E, P] = FinalEP(e, lb)
final override def isEPS: Boolean = true
final override def asEPS: EPS[E, P] = this
final override def toEPS: Option[EPS[E, P]] = Some(this)
}
/**
* Provides a factory for [[EPS]] objects.
*
* @author Michael Eichberg
*/
object EPS {
def apply[E <: Entity, P <: Property](e: E, lb: P, ub: P): EPS[E, P] = {
if (lb == ub) {
if (lb == null /* && | || ub == null*/ ) {
throw new IllegalArgumentException(s"lb and ub are null")
} else {
FinalEP(e, ub)
}
} else if (lb == null)
InterimEUBP(e, ub)
else if (ub == null)
InterimELBP(e, lb)
else
InterimELUBP(e, lb, ub)
}
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[E] = Some(eps.e)
}
/**
* Factory and extractor for [[EPS]] objects.
*
* @author Michael Eichberg
*/
object SomeEPS {
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, PropertyKey[P])] = {
Some((eps.e, eps.pk))
}
}
object LBPS {
/**
* Returns the tuple `(LowerBound, Boolean)`.
*
* @note Using LBPS to extract a property for which no lower bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(P, Boolean)] = {
Some((eps.lb, eps.isFinal))
}
}
object UBPS {
/**
* Returns the tuple `(UpperBound, Boolean)`.
*
* @note Using UBPS to extract a property for which no upper bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(P, Boolean)] = {
Some((eps.ub, eps.isFinal))
}
}
object LUBPS {
/**
* Returns the triple `(LowerBound, UpperBound, Boolean)`.
*
* @note Using LUBPS to extract a property for which no lower or upper bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(P, P, Boolean)] = {
Some((eps.lb, eps.ub, eps.isFinal))
}
}
/**
* Provides an extractor for [[EPS]] objects.
*
* @author Michael Eichberg
*/
object ELUBPS {
/**
* Returns the quadruple `(Entity, LowerBound, UpperBound, Boolean)`.
*
* @note Using ELUBPS to extract a property for which no lower or upper bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P, P, Boolean)] = {
Some((eps.e, eps.lb, eps.ub, eps.isFinal))
}
}
object ELUBP {
/**
* Returns the triple `(Entity, lowerBound : Property, upperBound : Property)`.
*
* @note Using ELUBP to extract a property for which no lower or upper bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P, P)] = {
Some((eps.e, eps.lb, eps.ub))
}
}
object LUBP {
/**
* Returns the pair `(lowerBound : Property, upperBound : Property)`.
*
* @note Using LUBP to extract a property for which no lower or upper bound was computed
* will (deliberately) result in an exception!
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(P, P)] = {
Some((eps.lb, eps.ub))
}
}
object EUBPS {
/**
* Returns the triple `(Entity, upperBound : Property, isFinal : Boolean)`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P, Boolean)] = {
Some((eps.e, eps.ub, eps.isFinal))
}
}
object EUBP {
/**
* Returns the pair `(Entity, upperBound : Property)`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P)] = {
Some((eps.e, eps.ub))
}
}
object UBP {
/**
* Returns the `lowerBound : Property`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[P] = Some(eps.ub)
}
object ELBPS {
/**
* Returns the triple `(Entity, lowerBound : Property, isFinal : Boolean)`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P, Boolean)] = {
Some((eps.e, eps.lb, eps.isFinal))
}
}
object ELBP {
/**
* Returns the pair `(Entity, lowerBound : Property)`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[(E, P)] = {
Some((eps.e, eps.lb))
}
}
object LBP {
/**
* Returns the `lowerBound : Property`.
*/
def unapply[E <: Entity, P <: Property](eps: EPS[E, P]): Some[P] = Some(eps.lb)
}
/**
* Encapsulate the final property `P` for the entity `E`.
*
* For a detailed discussion of the semantics of `lb` and `ub` see [[EOptionP.ub]].
*/
final class FinalEP[+E <: Entity, +P <: Property](val e: E, val p: P) extends EPS[E, P] {
override lazy val pk: PropertyKey[P] = p.key.asInstanceOf[PropertyKey[P]]
override def isFinal: Boolean = true
override def asFinal: FinalEP[E, P] = this
override private[fpcf] def toFinalEP: FinalEP[E, P] = this
override def hasLBP: Boolean = true
override def lb: P = p
override def toFinalELBP: FinalEP[E, P] = this
override def hasUBP: Boolean = true
override def ub: P = p
override def toFinalEUBP: FinalEP[E, P] = this
override def asInterim: InterimEP[E, P] = throw new ClassCastException();
override private[fpcf] def isUpdatedComparedTo(
oldEOptionP: EOptionP[Entity, Property]
): Boolean = {
oldEOptionP.isRefinable
}
private[fpcf] def checkIsValidPropertiesUpdate(
eps: SomeEPS,
newDependees: Iterable[SomeEOptionP]
): Unit = {
throw new IllegalArgumentException("already final")
}
override def equals(other: Any): Boolean = {
other match {
case that: FinalEP[_, _] => (this eq that) || (that.e == this.e && this.p == that.p)
case _ => false
}
}
override lazy val hashCode: Int = e.hashCode() * 727 + p.hashCode()
override def toString: String = {
s"FinalEP($e@${System.identityHashCode(e).toHexString},p=$p)"
}
}
object FinalEP {
def apply[E <: Entity, P <: Property](e: E, p: P): FinalEP[E, P] = new FinalEP(e, p)
def unapply[E <: Entity, P <: Property](eps: FinalEP[E, P]): Some[(E, P)] = {
Some((eps.e, eps.p))
}
}
/**
* Factory and extractor for [[FinalEP]] objects.
*
* @author Michael Eichberg
*/
object SomeFinalEP {
def unapply[E <: Entity, P <: Property](finalEP: FinalEP[E, P]): Some[(E, PropertyKey[P])] = {
Some((finalEP.e, finalEP.pk))
}
}
object FinalP {
def unapply[E <: Entity, P >: Null <: Property](eps: FinalEP[E, P]): Some[P] = Some(eps.p)
}
object FinalE {
def unapply[E <: Entity, P <: Property](eps: FinalEP[E, P]): Some[E] = Some(eps.e)
}
sealed trait InterimEP[+E <: Entity, +P <: Property] extends EPS[E, P] {
override def isFinal: Boolean = false
override def asFinal: FinalEP[E, P] = throw new ClassCastException();
override def asInterim: InterimEP[E, P] = this
private[fpcf] def checkIsValidLBPropertyUpdate(eps: SomeEPS): Unit = {
val newLBAsOP = eps.lb.asOrderedProperty
val lbAsOP = lb.asInstanceOf[newLBAsOP.Self]
newLBAsOP.checkIsEqualOrBetterThan(e, lbAsOP)
}
private[fpcf] def checkIsValidUBPropertyUpdate(eps: SomeEPS): Unit = {
val ubAsOP = ub.asOrderedProperty
val newUBAsOP = eps.ub.asInstanceOf[ubAsOP.Self]
ubAsOP.checkIsEqualOrBetterThan(e, newUBAsOP)
}
override private[fpcf] def checkIsValidPropertiesUpdate(
eps: SomeEPS,
newDependees: Iterable[SomeEOptionP]
): Unit = {
try {
if (eps.isRefinable && (hasLBP != eps.hasLBP || hasUBP != eps.hasUBP)) {
throw new IllegalArgumentException(s"inconsistent property bounds: $this vs. $eps")
}
if (hasLBP && eps.lb.isOrderedProperty) {
checkIsValidLBPropertyUpdate(eps)
}
if (hasUBP && eps.ub.isOrderedProperty) {
checkIsValidUBPropertyUpdate(eps)
}
} catch {
case t: Throwable =>
val m = s"$e: illegal update oldLB: $lb vs. newLB=$eps.lb "+
newDependees.mkString("newDependees={", ", ", "}; cause=") + t.getMessage
throw new IllegalArgumentException(m, t)
}
}
}
object InterimE {
/**
* Extracts the entity of an interim property.
*
* @note When we just have an InterimP object, we don't know which properties (ub, lb or both)
* are available.
*/
def unapply[E <: Entity, P <: Property](interimP: InterimEP[E, P]): Some[E] = Some(interimP.e)
}
/**
* Encapsulates the intermediate lower- and upper bound related to the computation of the respective
* property kind for the entity `E`.
*
* For a detailed discussion of the semantics of `lb` and `ub` see [[EOptionP.ub]].
*/
final class InterimELUBP[+E <: Entity, +P <: Property](
val e: E,
val lb: P,
val ub: P
) extends InterimEP[E, P] {
assert(lb != null)
assert(ub != null)
if (PropertyStore.Debug && lb /*or ub*/ .isOrderedProperty) {
val ubAsOP = ub.asOrderedProperty
ubAsOP.checkIsEqualOrBetterThan(e, lb.asInstanceOf[ubAsOP.Self])
}
override lazy val pk: PropertyKey[P] = ub /* or lb */ .key.asInstanceOf[PropertyKey[P]]
override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, ub)
override def hasLBP: Boolean = true
override def hasUBP: Boolean = true
override private[fpcf] def isUpdatedComparedTo(
oldEOptionP: EOptionP[Entity, Property]
): Boolean = {
oldEOptionP.isEPK || oldEOptionP.lb != this.lb || oldEOptionP.ub != this.ub
}
override def equals(other: Any): Boolean = {
other match {
case that: InterimELUBP[_, _] =>
(this eq that) || (e == that.e && lb == that.lb && ub == that.ub)
case _ =>
false
}
}
override lazy val hashCode: Int = ((e.hashCode() * 31 + lb.hashCode()) * 31) + ub.hashCode()
override def toString: String = {
s"InterimELUBP($e@${System.identityHashCode(e).toHexString},lb=$lb,ub=$ub)"
}
}
object InterimELUBP {
def apply[E <: Entity, P <: Property](e: E, lb: P, ub: P): InterimELUBP[E, P] = {
assert(lb ne ub)
new InterimELUBP(e, lb, ub)
}
def unapply[E <: Entity, P >: Null <: Property](eps: InterimELUBP[E, P]): Some[(E, P, P)] = {
Some((eps.e, eps.lb, eps.ub))
}
}
object InterimLUBP {
def unapply[E <: Entity, P >: Null <: Property](eps: InterimELUBP[E, P]): Some[(P, P)] = {
Some((eps.lb, eps.ub))
}
}
final class InterimEUBP[+E <: Entity, +P <: Property](
val e: E,
val ub: P
) extends InterimEP[E, P] {
assert(ub != null)
override lazy val pk: PropertyKey[P] = ub.key.asInstanceOf[PropertyKey[P]]
override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, ub)
override def hasLBP: Boolean = false
override def hasUBP: Boolean = true
override def lb: Nothing = throw new UnsupportedOperationException();
override private[fpcf] def isUpdatedComparedTo(
oldEOptionP: EOptionP[Entity, Property]
): Boolean = {
oldEOptionP.isEPK || oldEOptionP.ub != this.ub
}
override def equals(other: Any): Boolean = {
other match {
case that: InterimEUBP[_, _] =>
(this eq that) || (this.e == that.e && this.ub == that.ub)
case _ =>
false
}
}
override lazy val hashCode: Int = e.hashCode() * 31 + ub.hashCode()
override def toString: String = {
s"InterimEUBP($e@${System.identityHashCode(e).toHexString},ub=$ub)"
}
}
/**
* Factory and extractor for `InterimEUBP` objects. The extractor also matches `InterimELUBP`
* objects, but will throw an exception for `InterimELBP` objects. If you want to match
* final and interim objects at the same time use the `E(LB|UB)PS` extractors.
*/
object InterimEUBP {
def apply[E <: Entity, P <: Property](e: E, ub: P): InterimEUBP[E, P] = {
new InterimEUBP(e, ub)
}
def unapply[E <: Entity, P >: Null <: Property](eps: InterimEP[E, P]): Some[(E, P)] = {
Some((eps.e, eps.ub))
}
}
/**
* Factory and extractor for `InterimEUBP` objects. The extractor also matches `InterimELUBP`
* objects, but will throw an exception for `InterimELBP` objects. If you want to match
* final and interim objects at the same time use the `E(LB|UB)PS` extractors.
*/
object InterimEUB {
def unapply[E <: Entity, P <: Property](eps: InterimEP[E, P]): Some[E] = {
if (!eps.hasUBP)
throw new IllegalArgumentException(s"$eps does not define an upper bound property");
Some(eps.e)
}
}
/**
* Defines an extractor that matches EPKs and Interim properties where the latter only defines an
* upper bound, but does not define a lower bound.
*
* For example, an analysis which declares that it can only handle lower bounds, but can't process
* EPSs which only define an upper bound, may still see EPS which define only the upper bound
* but can process them in the same way as an EPK; that is, it can basically ignore the
* upper bound information. The scheduler ensures that the analysis scenario is a valid one
* and no cyclic dependent computations may arise.
* {{{
* def continuation(eps : EPS) : ... = eps match {
* case FinalEP(...) => // Matches only final properties.
* case LBP(...) => // Matches final and interim properties which define a lower bound.
* case NoLBP(...) => // Matches EPKs and EPS which just define an upper bound.
* }
* }}}
*/
object NoLBP {
def unapply[E <: Entity, P >: Null <: Property](eps: EOptionP[E, P]): Option[EOptionP[E, P]] = {
if (!eps.hasLBP)
Some(eps)
else
None
}
}
/**
* Defines an extractor that matches EPKs and Interim properties where the latter only defines a
* lower bound, but does not define an upper bound.
*/
object NoUBP {
def unapply[E <: Entity, P >: Null <: Property](eps: EOptionP[E, P]): Option[EOptionP[E, P]] = {
if (!eps.hasUBP)
Some(eps)
else
None
}
}
object InterimUBP {
def unapply[E <: Entity, P <: Property](eps: InterimEP[E, P]): Some[P] = Some(eps.ub)
}
final class InterimELBP[+E <: Entity, +P <: Property](
val e: E,
val lb: P
) extends InterimEP[E, P] {
assert(lb != null)
override lazy val pk: PropertyKey[P] = lb.key.asInstanceOf[PropertyKey[P]]
override private[fpcf] def toFinalEP: FinalEP[E, P] = FinalEP(e, lb)
override def hasLBP: Boolean = true
override def hasUBP: Boolean = false
override def ub: Nothing = throw new UnsupportedOperationException();
override private[fpcf] def isUpdatedComparedTo(
oldEOptionP: EOptionP[Entity, Property]
): Boolean = {
oldEOptionP.isEPK || oldEOptionP.lb != this.lb
}
override def equals(other: Any): Boolean = {
other match {
case that: InterimELBP[_, _] =>
(this eq that) || (this.lb == that.lb && this.e == that.e)
case _ =>
false
}
}
override lazy val hashCode: Int = e.hashCode() * 31 + lb.hashCode()
override def toString: String = {
s"InterimELBP($e@${System.identityHashCode(e).toHexString},lb=$lb)"
}
}
/**
* Factory and extractor for `InterimLBP` objects. The extractor also matches `InterimLUBP`
* objects, but will throw an exception for `InterimUBP` objects. If you want to match
* final and interim objects at the same time use the `E(LB|UB)PS` extractors.
*/
object InterimELBP {
def apply[E <: Entity, P <: Property](e: E, ub: P): InterimELBP[E, P] = {
new InterimELBP(e, ub)
}
def unapply[E <: Entity, P >: Null <: Property](eps: InterimEP[E, P]): Some[(E, P)] = {
Some((eps.e, eps.ub))
}
}
object InterimLBP {
def unapply[E <: Entity, P >: Null <: Property](eps: InterimEP[E, P]): Some[P] = Some(eps.ub)
}
/**
* A simple pair consisting of an [[Entity]] and a [[PropertyKey]].
*
* @author Michael Eichberg
*/
final class EPK[+E <: Entity, +P <: Property](
val e: E,
val pk: PropertyKey[P]
) extends EOptionP[E, P] {
override def hasLBP: Boolean = false
override def lb: Nothing = throw new UnsupportedOperationException();
override private[fpcf] def toFinalELBP = throw new UnsupportedOperationException(toString);
override def hasUBP: Boolean = false
override def ub: Nothing = throw new UnsupportedOperationException();
override private[fpcf] def toFinalEUBP = throw new UnsupportedOperationException(toString);
override def isFinal: Boolean = false
override def asFinal: FinalEP[E, P] = throw new ClassCastException();
override private[fpcf] def toFinalEP: FinalEP[E, P] = {
throw new UnsupportedOperationException(toString)
};
override def isEPS: Boolean = false
override def asEPS: EPS[E, P] = {
throw new ClassCastException()
};
override def isEPK: Boolean = true
override def asEPK: EPK[E, P] = this
override def toEPK: this.type = this
override def toEPS: Option[EPS[E, P]] = None
override def asInterim: InterimEP[E, P] = throw new ClassCastException();
override private[fpcf] def isUpdatedComparedTo(
oldEOptionP: EOptionP[Entity, Property]
): Boolean = {
false
}
override private[fpcf] def checkIsValidPropertiesUpdate(
eps: SomeEPS,
newDependees: Iterable[SomeEOptionP]
): Unit = {}
override def equals(other: Any): Boolean = {
other match {
case that: EPK[_, _] => (this eq that) || (this.pk == that.pk && that.e == this.e)
case _ => false
}
}
override lazy val hashCode: Int = e.hashCode() * 31 + pk.id
override def toString: String = {
val pkId = pk.id
val pkName = PropertyKey.name(pkId)
s"EPK($e@${System.identityHashCode(e).toHexString},$pkName#$pkId)"
}
}
/**
* Factory and extractor for [[EPK]] objects.
*
* @author Michael Eichberg
*/
object EPK {
def apply[E <: Entity, P <: Property](e: E, pk: PropertyKey[P]): EPK[e.type, P] = new EPK(e, pk)
def apply[E <: Entity, P <: Property](e: E, p: P): EPK[E, P] = {
new EPK(e, p.key.asInstanceOf[PropertyKey[P]])
}
def unapply[E <: Entity, P >: Null <: Property](epk: EPK[E, P]): Some[(E, PropertyKey[P])] = {
Some((epk.e, epk.pk))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy