All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.coursier.core.DependencySet.scala Maven / Gradle / Ivy

The newest version!
package coursier.core

import coursier.core.DependencySet.Sets

import scala.collection.compat._
import scala.collection.immutable.IntMap
import scala.collection.mutable

final class DependencySet private (
  val set: Set[Dependency],
  grouped: Map[Dependency, Sets[Dependency]]
) extends Product {

  def canEqual(that: Any): Boolean =
    that.isInstanceOf[DependencySet]

  def productArity: Int = 1
  //   Add that back if / when dropping Scala 2.12 support
  // override def productElementName(n: Int): String =
  //   if (n == 0) "set"
  //   else throw new NoSuchElementException(s"Element at index $n in DependencySet")
  def productElement(n: Int): Any =
    if (n == 0) set
    else throw new NoSuchElementException(s"Element at index $n in DependencySet")

  override def equals(obj: Any): Boolean =
    obj match {
      case other: DependencySet =>
        set == other.set
      case _ => false
    }

  override lazy val hashCode: Int = {
    var code = 17 + "coursier.core.DependencySet".##
    code = 37 * code + set.##
    37 * code
  }

  override def toString: String =
    s"DependencySet($set, ${grouped.size} groups)"

//  assert(grouped.iterator.map(_._2.size).sum == set.size, s"${grouped.iterator.map(_._2.size).sum} != ${set.size}")
//  assert(grouped.forall { case (dep, l) => l.forall(_.moduleVersion == dep.moduleVersion) })

  lazy val minimizedSet: Set[Dependency] =
    grouped.iterator.flatMap(_._2.children.keysIterator).toSet

  def contains(dependency: Dependency): Boolean = {
    val dep0 = dependency.clearExclusions
    val set  = grouped.getOrElse(dep0, Sets.empty[Dependency])
    set.contains(dependency)
  }

  def covers(dependency: Dependency): Boolean = {
    val dep0 = dependency.clearExclusions
    val set  = grouped.getOrElse(dep0, Sets.empty[Dependency])
    set.covers(
      dependency,
      _.minimizedExclusions.size(),
      (a, b) => a.minimizedExclusions.subsetOf(b.minimizedExclusions)
    )
  }

  def add(dependency: Dependency): DependencySet =
    add(Seq(dependency))

  def add(dependency: Iterable[Dependency]): DependencySet =
    addNoCheck(dependency.filter(!set(_)))

  private def addNoCheck(dependencies: Iterable[Dependency]): DependencySet =
    if (dependencies.isEmpty)
      this
    else {
      var m = grouped
      for (dep <- dependencies) {
        val dep0 = dep.clearExclusions
        // Optimized map value addition. Only mutate map if we made a change
        m.get(dep0) match {
          case None =>
            m = m + (dep0 -> Sets.empty[Dependency].add(
              dep,
              _.minimizedExclusions.size(),
              (a, b) => a.minimizedExclusions.subsetOf(b.minimizedExclusions)
            ))
          case Some(groupSet) =>
            val groupSet0 = groupSet.add(
              dep,
              _.minimizedExclusions.size(),
              (a, b) => a.minimizedExclusions.subsetOf(b.minimizedExclusions)
            )
            if (groupSet ne groupSet0)
              m = m + (dep0 -> groupSet0)
        }
      }
      new DependencySet(set ++ dependencies, m)
    }

  def remove(dependencies: Iterable[Dependency]): DependencySet =
    removeNoCheck(dependencies.filter(set))

  private def removeNoCheck(dependencies: Iterable[Dependency]): DependencySet =
    if (dependencies.isEmpty)
      this
    else {
      var m = grouped
      for (dep <- dependencies) {
        val dep0 = dep.clearExclusions
        // getOrElse useful if we're passed duplicated stuff in dependencies
        val prev = m.getOrElse(dep0, Sets.empty)
        if (prev.contains(dep))
          if (prev.size <= 1)
            m -= dep0
          else {
            val l = prev
              .remove(
                dep,
                _.minimizedExclusions.size(),
                (a, b) => a.minimizedExclusions.subsetOf(b.minimizedExclusions)
              )
            m += ((dep0, l))
          }
      }

      new DependencySet(set -- dependencies, m.toMap)
    }

  def setValues(newSet: Set[Dependency]): DependencySet = {
    val toAdd    = newSet -- set
    val toRemove = set -- newSet
    addNoCheck(toAdd)
      .removeNoCheck(toRemove)
  }

}

object DependencySet {
  val empty = new DependencySet(Set.empty, Map.empty)

  private object Sets {
    def empty[T]: Sets[T] = Sets(IntMap.empty, Map.empty, Map.empty)
  }

  private final case class Sets[T] private (
    required: IntMap[Set[T]],
    children: Map[T, Set[T]],
    parents: Map[T, T]
  ) {

    def size: Int = parents.size + children.size

    def forall(f: T => Boolean): Boolean =
      (parents.keysIterator ++ children.keysIterator)
        .forall(f)

    def contains(t: T): Boolean =
      parents.contains(t) || children.contains(t)

    def covers(t: T, size: T => Int, subsetOf: (T, T) => Boolean): Boolean =
      contains(t) || {
        val n = size(t)
        required.view.filterKeys(_ <= n).iterator.flatMap(_._2.iterator).exists {
          s0 => subsetOf(s0, t)
        }
      }

    private def forceAdd(s: T, size: T => Int, subsetOf: (T, T) => Boolean): Sets[T] = {

      val n = size(s)

      val subsetOpt = required.view.filterKeys(_ <= n).iterator.flatMap(_._2.iterator).find {
        s0 => subsetOf(s0, s)
      }

      subsetOpt match {
        case None =>
          val required0 = required + (n -> (required.getOrElse(n, Set.empty) + s))
          val children0 = children + (s -> Set.empty[T])
          Sets(required0, children0, parents)
        case Some(subset) =>
          val children0 = children + (subset -> (children.getOrElse(subset, Set.empty) + s))
          val parents0  = parents + (s       -> subset)
          Sets(required, children0, parents0)
      }
    }

    def add(s: T, size: T => Int, subsetOf: (T, T) => Boolean): Sets[T] =
      if (children.contains(s) || parents.contains(s))
        this
      else
        forceAdd(s, size, subsetOf)

    def remove(s: T, size: T => Int, subsetOf: (T, T) => Boolean): Sets[T] =
      children.get(s) match {
        case Some(children0) =>
          val required0 = {
            val elem = required.getOrElse(size(s), Set.empty) - s
            if (elem.isEmpty)
              required - size(s)
            else
              required + (size(s) -> elem)
          }
          val parents0 = parents -- children0
          val sets0    = Sets(required0, children - s, parents0)
          children0.foldLeft(sets0)(_.forceAdd(_, size, subsetOf))
        case None =>
          parents.get(s) match {
            case Some(parent) =>
              Sets(
                required,
                children + (parent -> (children.getOrElse(parent, Set.empty) - s)),
                parents - s
              )
            case None =>
              this
          }
      }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy