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

scroll.internal.graph.impl.ScalaRoleGraph.scala Maven / Gradle / Ivy

package scroll.internal.graph.impl

import com.google.common.graph.Graph
import com.google.common.graph.GraphBuilder
import com.google.common.graph.Graphs
import com.google.common.graph.MutableGraph
import scroll.internal.compartment.impl.AbstractCompartment
import scroll.internal.graph.RoleGraph

import scala.annotation.tailrec
import scala.jdk.CollectionConverters._
import scala.util.Failure
import scala.util.Success
import scala.util.Try

object ScalaRoleGraph {

  def copyFrom(from: ScalaRoleGraph, checkForCycles: Boolean): ScalaRoleGraph =
    new ScalaRoleGraph(from.root, checkForCycles)

}

/** Scala specific implementation of a [[scroll.internal.graph.RoleGraph]] using a graph as underlying data model.
  *
  * @param checkForCycles
  *   set to true to forbid cyclic role playing relationships
  */
class ScalaRoleGraph(
  val root: MutableGraph[Object] = GraphBuilder.directed().build[Object](),
  val checkForCycles: Boolean = true
) extends RoleGraph {

  protected val MERGE_MESSAGE: String = "You can only merge RoleGraphs of the same type!"

  override def addPart(other: RoleGraph): Boolean = {
    require(other.isInstanceOf[ScalaRoleGraph], MERGE_MESSAGE)

    val target = other.asInstanceOf[ScalaRoleGraph].root
    if (!target.nodes().isEmpty) {
      target
        .edges()
        .forEach { p =>
          val _ = root.putEdge(p.source(), p.target())
        }
      checkCycles()
      true
    } else {
      false
    }
  }

  override def detach(other: RoleGraph): Unit = {
    require(null != other)
    val target = other.asInstanceOf[ScalaRoleGraph].root
    target
      .edges()
      .forEach { p =>
        removeBinding(p.source(), p.target())
      }
  }

  private def checkCycles(): Unit =
    if (checkForCycles && Graphs.hasCycle(root)) {
      throw new RuntimeException("Cyclic role-playing relationship found!")
    }

  override def addBinding(player: AnyRef, role: AnyRef): Unit = {
    require(null != player)
    require(null != role)
    val _ = root.putEdge(player, role)
    if (checkForCycles && Graphs.hasCycle(root)) {
      throw new RuntimeException(s"Cyclic role-playing relationship for player '$player' found!")
    }
  }

  override def removeBinding(player: AnyRef, role: AnyRef): Unit = {
    val _ = root.removeEdge(player, role)
  }

  override def removePlayer(player: AnyRef): Unit = {
    val _ = root.removeNode(player)
  }

  private def filter(g: Graph[AnyRef], player: AnyRef): Seq[AnyRef] =
    Try(Graphs.reachableNodes(g, player)) match {
      case Success(nodes) =>
        val buffer = new java.util.LinkedList(nodes)
        buffer.remove(player)
        buffer.asScala.toSeq
      case Failure(_) => Seq.empty[AnyRef]
    }

  override def roles(player: AnyRef): Seq[AnyRef] = filter(root, player)

  override def facets(player: AnyRef): Seq[Enumeration#Value] =
    if (containsPlayer(player)) {
      root.successors(player).asScala.collect { case v: Enumeration#Value => v }.toSeq
    } else {
      Seq.empty[Enumeration#Value]
    }

  override def containsPlayer(player: AnyRef): Boolean = root.nodes().contains(player)

  override def allPlayers: Seq[AnyRef] = root.nodes().asScala.toSeq

  override def predecessors(player: AnyRef): Seq[AnyRef] = filter(Graphs.transpose(root), player)

  @tailrec
  final override def coreFor(role: AnyRef): Seq[AnyRef] = {
    require(null != role)
    role match {
      case cur: AbstractCompartment#IPlayer[?, ?] => coreFor(cur.wrapped)
      case cur: AnyRef if containsPlayer(cur) =>
        predecessors(cur) match {
          case Nil         => Seq(cur)
          case head +: Nil => coreFor(head)
          case r           => r
        }
      case _ => Seq.empty[AnyRef]
    }
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy