You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

241 lines
6.9 KiB
Kotlin

package net.cijber.pubgrub.version
import net.cijber.pubgrub.SetRelation
import net.cijber.pubgrub.stubs.Version
open class VersionRange<V : Version<V>>(
val bottom: VersionBorder<V>?,
val top: VersionBorder<V>?
) :
VersionConstraint<V> {
override val isEmpty: Boolean = false
override val isAny: Boolean = bottom == null && top == null
infix fun relation(rhs: VersionRange<V>): SetRelation {
val topDiff = when {
top != null && rhs.top != null -> top.compareTo(rhs.top)
top != null -> -1
rhs.top != null -> 1
else -> 0
}
val bottomDiff = when {
bottom != null && rhs.bottom != null -> bottom.compareTo(rhs.bottom)
bottom != null -> 1
rhs.bottom != null -> -1
else -> 0
}
if (topDiff == 0 && bottomDiff == 0) {
return SetRelation.Equal
}
if (topDiff == 1 && bottomDiff == -1) {
return SetRelation.Superset
}
if (topDiff <= 0 && bottomDiff >= 0) {
return SetRelation.Subset
}
val topBottomDiff = when {
top != null && rhs.bottom != null -> top.compareTo(rhs.bottom)
else -> 1
}
val bottomTopDiff = when {
bottom != null && rhs.top != null -> bottom.compareTo(rhs.top)
else -> -1
}
if (bottomTopDiff == 1 || topBottomDiff == -1) {
return SetRelation.Disjoint
}
return SetRelation.Overlaps
}
override fun allowsAll(rhs: VersionConstraint<V>): Boolean {
if (rhs.isEmpty)
return true
if (rhs.isAny)
return false
if (rhs is VersionRange) {
return relation(rhs) in setOf(SetRelation.Equal, SetRelation.Superset)
}
if (rhs is VersionUnion) {
return rhs.constraints.all(this::allowsAll)
}
error("Unknown VersionConstraint: $rhs")
}
override fun allowsAny(rhs: VersionConstraint<V>): Boolean {
if (rhs.isEmpty || rhs.isAny)
return true
if (rhs is VersionRange) {
return relation(rhs) != SetRelation.Disjoint
}
if (rhs is VersionUnion) {
return rhs.constraints.any(this::allowsAny)
}
error("Unknown VersionConstraint: $rhs")
}
override fun allows(version: V): Boolean {
return (bottom?.beneath(version, true) ?: true) &&
(top?.above(version, true) ?: true)
}
override fun intersect(rhs: VersionConstraint<V>): VersionConstraint<V> {
if (rhs.isEmpty) {
return rhs
}
if (rhs.isAny) {
return this
}
if (rhs is VersionUnion) {
return rhs.intersect(this)
}
if (rhs is VersionRange) {
if (relation(rhs) != SetRelation.Disjoint) {
return VersionRange(
when {
rhs.bottom != null && bottom != null -> maxOf(rhs.bottom, bottom)
else -> bottom ?: rhs.bottom
},
when {
rhs.top != null && top != null -> minOf(rhs.top, top)
else -> top ?: rhs.top
}
)
}
return VersionEmpty()
}
error("Unkown VersionConstraint: $rhs")
}
override fun union(rhs: VersionConstraint<V>): VersionConstraint<V> {
if (rhs is VersionRange) {
return when (relation(rhs)) {
SetRelation.Superset, SetRelation.Equal -> this
SetRelation.Subset -> rhs
SetRelation.Overlaps -> VersionRange(
when {
rhs.bottom != null && bottom != null -> minOf(rhs.bottom, bottom)
else -> null
},
when {
rhs.top != null && top != null -> maxOf(rhs.top, top)
else -> null
}
)
SetRelation.Disjoint -> VersionUnion(listOf(this, rhs))
}
}
if (rhs is VersionEmpty) {
return this
}
if (rhs is VersionUnion) {
return VersionUnion(rhs.constraints + listOf(this))
}
error("Unknown VersionConstraint given: $rhs")
}
override fun difference(rhs: VersionConstraint<V>): VersionConstraint<V> {
if (rhs.isEmpty) {
return this
}
if (rhs.isAny) {
return VersionEmpty()
}
if (rhs is VersionUnion) {
val ranges = mutableListOf(this)
for (constraint in rhs.constraints) {
val rel = constraint relation ranges.last()
if (rel == SetRelation.Disjoint) {
continue
}
val last = ranges.dropLast(1).first()
val res = last.difference(constraint)
if (res.isEmpty) {
break
}
if (res is VersionUnion) {
ranges.addAll(res.constraints)
}
if (res is VersionRange) {
ranges.add(res)
}
}
return when (ranges.count()) {
0 -> VersionEmpty()
1 -> ranges.first()
else -> VersionUnion(ranges)
}
}
if (rhs is VersionRange) {
return when (rhs relation this) {
SetRelation.Disjoint -> this
SetRelation.Equal, SetRelation.Superset -> VersionEmpty()
SetRelation.Overlaps -> VersionRange(
when {
bottom != null && rhs.bottom != null && rhs.bottom < bottom -> rhs.top
else -> bottom
},
when {
top != null && rhs.top != null && rhs.top > top -> rhs.bottom
else -> top
}
)
SetRelation.Subset -> {
if (top == rhs.top) {
return VersionRange(bottom, rhs.bottom?.reversed())
}
if (bottom == rhs.bottom) {
return VersionRange(rhs.top?.reversed(), top)
}
return VersionUnion(
listOf(
VersionRange(bottom, rhs.bottom?.reversed()),
VersionRange(rhs.top?.reversed(), top)
)
)
}
}
}
error("Unknown VersionConstraint given: $rhs")
}
companion object {
fun <V : Version<V>> single(version: V) =
VersionRange(VersionBorder.bottom(version), VersionBorder.top(version))
}
}