alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (Diagram.scala)

This example Scala source code file (Diagram.scala) is included in my "Source Code Warehouse" project. The intent of this project is to help you more easily find Scala source code examples by using tags.

All credit for the original source code belongs to scala-lang.org; I'm just trying to make examples easier to find. (For my Scala work, see my Scala examples and tutorials.)

Scala tags/keywords

compiler, contentdiagram, contentdiagramdepth, depthinfo, diagram, list, map, node, none, nsc, option, typeentity

The Diagram.scala Scala example source code

package scala.tools.nsc.doc
package model
package diagram

import model._

/**
 *  The diagram base classes
 *
 *  @author Damien Obrist
 *  @author Vlad Ureche
 */
sealed abstract class Diagram {
  def nodes: List[Node]
  def edges: List[(Node, List[Node])]
  def isContentDiagram = false     // Implemented by ContentDiagram
  def isInheritanceDiagram = false // Implemented by InheritanceDiagram
  def depthInfo: DepthInfo
}

case class ContentDiagram(nodes:List[/*Class*/Node], edges:List[(Node, List[Node])]) extends Diagram {
  override def isContentDiagram = true
  lazy val depthInfo = new ContentDiagramDepth(this)
}

/** A class diagram */
case class InheritanceDiagram(thisNode: ThisNode,
                        superClasses: List[/*Class*/Node],
                        subClasses: List[/*Class*/Node],
                        incomingImplicits: List[ImplicitNode],
                        outgoingImplicits: List[ImplicitNode]) extends Diagram {
  def nodes = thisNode :: superClasses ::: subClasses ::: incomingImplicits ::: outgoingImplicits
  def edges = (thisNode -> (superClasses ::: outgoingImplicits)) ::
              (subClasses ::: incomingImplicits).map(_ -> List(thisNode))

  override def isInheritanceDiagram = true
  lazy val depthInfo = new DepthInfo {
    def maxDepth = 3
  }
}

trait DepthInfo {
  /** Gives the maximum depth */
  def maxDepth: Int
}

sealed abstract class Node {
  def name = tpe.name
  def tpe: TypeEntity
  def tpl: Option[TemplateEntity]
  /** shortcut to get a DocTemplateEntity */
  def doctpl: Option[DocTemplateEntity] = tpl match {
    case Some(tpl) => tpl match {
      case d: DocTemplateEntity => Some(d)
      case _ => None
    }
    case _ => None
  }
  /* shortcuts to find the node type without matching */
  def isThisNode = false
  def isNormalNode = false
  def isClassNode = if (tpl.isDefined) (tpl.get.isClass || tpl.get.qualifiedName == "scala.AnyRef") else false
  def isTraitNode = if (tpl.isDefined) tpl.get.isTrait else false
  def isObjectNode= if (tpl.isDefined) tpl.get.isObject else false
  def isTypeNode  = if (doctpl.isDefined) doctpl.get.isAbstractType || doctpl.get.isAliasType else false
  def isOtherNode = !(isClassNode || isTraitNode || isObjectNode || isTypeNode)
  def isImplicitNode = false
  def isOutsideNode = false
  def tooltip: Option[String]
}

// different matchers, allowing you to use the pattern matcher against any node
// NOTE: A ThisNode or ImplicitNode can at the same time be ClassNode/TraitNode/OtherNode, not exactly according to
// case class specification -- thus a complete match would be:
//   node match {
//     case ThisNode(tpe, _) =>     /* case for this node, you can still use .isClass, .isTrait and .isOther */
//     case ImplicitNode(tpe, _) => /* case for an implicit node, you can still use .isClass, .isTrait and .isOther */
//     case _ => node match {
//       case ClassNode(tpe, _) =>  /* case for a non-this, non-implicit Class node */
//       case TraitNode(tpe, _) =>  /* case for a non-this, non-implicit Trait node */
//       case OtherNode(tpe, _) =>  /* case for a non-this, non-implicit Other node */
//     }
//   }
object Node        { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = Some((n.tpe, n.tpl)) }
object ClassNode   { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isClassNode)   Some((n.tpe, n.tpl)) else None }
object TraitNode   { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTraitNode)   Some((n.tpe, n.tpl)) else None }
object TypeNode    { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isTypeNode)    Some((n.tpe, n.tpl)) else None }
object ObjectNode  { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isObjectNode)  Some((n.tpe, n.tpl)) else None }
object OutsideNode { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOutsideNode) Some((n.tpe, n.tpl)) else None }
object OtherNode   { def unapply(n: Node): Option[(TypeEntity, Option[TemplateEntity])] = if (n.isOtherNode)   Some((n.tpe, n.tpl)) else None }



/** The node for the current class */
case class ThisNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isThisNode = true }

/** The usual node */
case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isNormalNode = true }

/** A class or trait the thisnode can be converted to by an implicit conversion
 *  TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes
 *  since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate
 */
case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }

/** An outside node is shown in packages when a class from a different package makes it to the package diagram due to
 * its relation to a class in the template (see @contentDiagram hideInheritedNodes annotation) */
case class OutsideNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isOutsideNode = true }


// Computing and offering node depth information
class ContentDiagramDepth(pack: ContentDiagram) extends DepthInfo {
  private[this] var _maxDepth = 0
  private[this] var _nodeDepth = Map[Node, Int]()
  private[this] var seedNodes = Set[Node]()
  private[this] val invertedEdges: Map[Node, List[Node]] =
    pack.edges.flatMap({case (node: Node, outgoing: List[Node]) => outgoing.map((_, node))}).groupBy(_._1).map({case (k, values) => (k, values.map(_._2))}).withDefaultValue(Nil)
  private[this] val directEdges: Map[Node, List[Node]] = pack.edges.toMap.withDefaultValue(Nil)

  // seed base nodes, to minimize noise - they can't all have parents, else there would only be cycles
  seedNodes ++= pack.nodes.filter(directEdges(_).isEmpty)

  while (!seedNodes.isEmpty) {
    var newSeedNodes = Set[Node]()
    for (node <- seedNodes) {
      val depth = 1 + (-1 :: directEdges(node).map(_nodeDepth.getOrElse(_, -1))).max
      if (depth != _nodeDepth.getOrElse(node, -1)) {
        _nodeDepth += (node -> depth)
        newSeedNodes ++= invertedEdges(node)
        if (depth > _maxDepth) _maxDepth = depth
      }
    }
    seedNodes = newSeedNodes
  }

  val maxDepth = _maxDepth
}

Other Scala source code examples

Here is a short list of links related to this Scala Diagram.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.