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

Scala example source code file (DiagramDirectiveParser.scala)

This example Scala source code file (DiagramDirectiveParser.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

boolean, compiler, diagramfilter, doctemplateimpl, fulldiagram, list, nil, node, nodespecregex, nodiagramatall, nsc, string, utilities

The DiagramDirectiveParser.scala Scala example source code

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

import model._
import java.util.regex.{Pattern, Matcher}
import scala.util.matching.Regex

/**
 *  This trait takes care of parsing @{inheritance, content}Diagram annotations
 *
 *  @author Damien Obrist
 *  @author Vlad Ureche
 */
trait DiagramDirectiveParser {
  this: ModelFactory with DiagramFactory with CommentFactory with TreeFactory =>

  import this.global.definitions.AnyRefClass

  ///// DIAGRAM FILTERS //////////////////////////////////////////////////////////////////////////////////////////////

  /**
   *  The DiagramFilter trait directs the diagram engine about the way the diagram should be displayed
   *
   *  Vlad: There's an explanation I owe to people using diagrams and not finding a way to hide a specific class from
   *  all diagrams at once. So why did I choose to allow you to only control the diagrams at class level? So, the
   *  reason is you would break the separate scaladoc compilation:
   *  If you have an "@diagram hideMyClass" annotation in class A and you run scaladoc on it along with its subclass B
   *  A will not appear in B's diagram. But if you scaladoc only on B, A's comment will not be parsed and the
   *  instructions to hide class A from all diagrams will not be available. Thus I prefer to force you to control the
   *  diagrams of each class locally. The problem does not appear with scalac, as scalac stores all its necessary
   *  information (like scala signatures) serialized in the .class file. But we couldn't store doc comments in the class
   *  file, could we? (Turns out we could, but that's another story)
   *
   *  Any flaming for this decision should go to scala-internals@googlegroups.com
   */
  trait DiagramFilter {
    /** A flag to hide the diagram completely */
    def hideDiagram: Boolean
    /** Hide incoming implicit conversions (for type hierarchy diagrams) */
    def hideIncomingImplicits: Boolean
    /** Hide outgoing implicit conversions (for type hierarchy diagrams) */
    def hideOutgoingImplicits: Boolean
    /** Hide superclasses (for type hierarchy diagrams) */
    def hideSuperclasses: Boolean
    /** Hide subclasses (for type hierarchy diagrams) */
    def hideSubclasses: Boolean
    /** Show related classes from other objects/traits/packages (for content diagrams) */
    def hideInheritedNodes: Boolean
    /** Hide a node from the diagram */
    def hideNode(clazz: Node): Boolean
    /** Hide an edge from the diagram */
    def hideEdge(clazz1: Node, clazz2: Node): Boolean
  }

  /** Main entry point into this trait: generate the filter for inheritance diagrams */
  def makeInheritanceDiagramFilter(template: DocTemplateImpl): DiagramFilter = {

    val defaultFilter =
      if (template.isClass || template.isTrait || template.sym == AnyRefClass)
        FullDiagram
      else
        NoDiagramAtAll

    if (template.comment.isDefined)
      makeDiagramFilter(template, template.comment.get.inheritDiagram, defaultFilter, isInheritanceDiagram = true)
    else
      defaultFilter
  }

  /** Main entry point into this trait: generate the filter for content diagrams */
  def makeContentDiagramFilter(template: DocTemplateImpl): DiagramFilter = {
    val defaultFilter = if (template.isPackage || template.isObject) FullDiagram else NoDiagramAtAll
    if (template.comment.isDefined)
      makeDiagramFilter(template, template.comment.get.contentDiagram, defaultFilter, isInheritanceDiagram = false)
    else
      defaultFilter
  }

  protected var tFilter = 0l
  protected var tModel = 0l

  /** Show the entire diagram, no filtering */
  case object FullDiagram extends DiagramFilter {
    val hideDiagram: Boolean = false
    val hideIncomingImplicits: Boolean = false
    val hideOutgoingImplicits: Boolean = false
    val hideSuperclasses: Boolean = false
    val hideSubclasses: Boolean = false
    val hideInheritedNodes: Boolean = false
    def hideNode(clazz: Node): Boolean = false
    def hideEdge(clazz1: Node, clazz2: Node): Boolean = false
  }

  /** Hide the diagram completely, no need for special filtering */
  case object NoDiagramAtAll extends DiagramFilter {
    val hideDiagram: Boolean = true
    val hideIncomingImplicits: Boolean = true
    val hideOutgoingImplicits: Boolean = true
    val hideSuperclasses: Boolean = true
    val hideSubclasses: Boolean = true
    val hideInheritedNodes: Boolean = true
    def hideNode(clazz: Node): Boolean = true
    def hideEdge(clazz1: Node, clazz2: Node): Boolean = true
  }

  /** The AnnotationDiagramFilter trait directs the diagram engine according to an annotation
   *  TODO: Should document the annotation, for now see parseDiagramAnnotation in ModelFactory.scala */
  case class AnnotationDiagramFilter(hideDiagram: Boolean,
                                             hideIncomingImplicits: Boolean,
                                             hideOutgoingImplicits: Boolean,
                                             hideSuperclasses: Boolean,
                                             hideSubclasses: Boolean,
                                             hideInheritedNodes: Boolean,
                                             hideNodesFilter: List[Pattern],
                                             hideEdgesFilter: List[(Pattern, Pattern)]) extends DiagramFilter {

    private[this] def getName(n: Node): String =
      if (n.tpl.isDefined)
        n.tpl.get.qualifiedName
      else
        n.name

    def hideNode(clazz: Node): Boolean = {
      val qualifiedName = getName(clazz)
      for (hideFilter <- hideNodesFilter)
        if (hideFilter.matcher(qualifiedName).matches) {
          // println(hideFilter + ".matcher(" + qualifiedName + ").matches = " + hideFilter.matcher(qualifiedName).matches)
          return true
        }
      false
    }

    def hideEdge(clazz1: Node, clazz2: Node): Boolean = {
      val clazz1Name = getName(clazz1)
      val clazz2Name = getName(clazz2)
      for ((clazz1Filter, clazz2Filter) <- hideEdgesFilter) {
        if (clazz1Filter.matcher(clazz1Name).matches &&
            clazz2Filter.matcher(clazz2Name).matches) {
          // println(clazz1Filter + ".matcher(" + clazz1Name + ").matches = " + clazz1Filter.matcher(clazz1Name).matches)
          // println(clazz2Filter + ".matcher(" + clazz2Name + ").matches = " + clazz2Filter.matcher(clazz2Name).matches)
          return true
        }
      }
      false
    }
  }

  // TODO: This could certainly be improved -- right now the only regex is *, but there's no way to match a single identifier
  private val NodeSpecRegex = "\\\"[A-Za-z\\*][A-Za-z\\.\\*]*\\\""
  private val NodeSpecPattern = Pattern.compile(NodeSpecRegex)
  private val EdgeSpecRegex = "\\(" + NodeSpecRegex + "\\s*\\->\\s*" + NodeSpecRegex + "\\)"
  // And the composed regexes:
  private val HideNodesRegex = new Regex("^hideNodes(\\s*" + NodeSpecRegex + ")+$")
  private val HideEdgesRegex = new Regex("^hideEdges(\\s*" + EdgeSpecRegex + ")+$")

  private def makeDiagramFilter(template: DocTemplateImpl,
                                directives: List[String],
                                defaultFilter: DiagramFilter,
                                isInheritanceDiagram: Boolean): DiagramFilter = directives match {

    // if there are no specific diagram directives, return the default filter (either FullDiagram or NoDiagramAtAll)
    case Nil =>
      defaultFilter

    // compute the exact filters. By including the annotation, the diagram is autmatically added
    case _ =>
      tFilter -= System.currentTimeMillis
      var hideDiagram0: Boolean = false
      var hideIncomingImplicits0: Boolean = false
      var hideOutgoingImplicits0: Boolean = false
      var hideSuperclasses0: Boolean = false
      var hideSubclasses0: Boolean = false
      var hideInheritedNodes0: Boolean = false
      var hideNodesFilter0: List[Pattern] = Nil
      var hideEdgesFilter0: List[(Pattern, Pattern)] = Nil

      def warning(message: String) = {
        // we need the position from the package object (well, ideally its comment, but yeah ...)
        val sym = if (template.sym.isPackage) template.sym.info.member(global.nme.PACKAGE) else template.sym
        assert((sym != global.NoSymbol) || (sym == global.rootMirror.RootPackage))
        global.reporter.warning(sym.pos, message)
      }

      def preparePattern(className: String) =
        "^" + className.stripPrefix("\"").stripSuffix("\"").replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*") + "$"

      // separate entries:
      val entries = directives.foldRight("")(_ + " " + _).split(",").map(_.trim)
      for (entry <- entries)
        entry match {
          case "hideDiagram" =>
              hideDiagram0 = true
          case "hideIncomingImplicits" if isInheritanceDiagram =>
              hideIncomingImplicits0 = true
          case "hideOutgoingImplicits" if isInheritanceDiagram  =>
              hideOutgoingImplicits0 = true
          case "hideSuperclasses" if isInheritanceDiagram =>
              hideSuperclasses0 = true
          case "hideSubclasses" if isInheritanceDiagram =>
              hideSubclasses0 = true
          case "hideInheritedNodes" if !isInheritanceDiagram =>
              hideInheritedNodes0 = true
          case HideNodesRegex(last) =>
            val matcher = NodeSpecPattern.matcher(entry)
            while (matcher.find()) {
              val classPattern = Pattern.compile(preparePattern(matcher.group()))
              hideNodesFilter0 ::= classPattern
            }
          case HideEdgesRegex(last) =>
            val matcher = NodeSpecPattern.matcher(entry)
            while (matcher.find()) {
              val class1Pattern = Pattern.compile(preparePattern(matcher.group()))
              assert(matcher.find()) // it's got to be there, just matched it!
              val class2Pattern = Pattern.compile(preparePattern(matcher.group()))
              hideEdgesFilter0 ::= ((class1Pattern, class2Pattern))
            }
          case "" =>
            // don't need to do anything about it
          case _ =>
            warning("Could not understand diagram annotation in " + template.kind + " " + template.qualifiedName +
              ": unmatched entry \"" + entry + "\".\n" +
              "  This could be because:\n" +
              "   - you forgot to separate entries by commas\n" +
              "   - you used a tag that is not allowed in the current context (like @contentDiagram hideSuperclasses)\n"+
              "   - you did not use one of the allowed tags (see docs.scala-lang.org for scaladoc annotations)")
        }
      val result =
        if  (hideDiagram0)
          NoDiagramAtAll
        else if ((hideNodesFilter0.isEmpty) &&
                 (hideEdgesFilter0.isEmpty) &&
                 (hideIncomingImplicits0 == false) &&
                 (hideOutgoingImplicits0 == false) &&
                 (hideSuperclasses0 == false) &&
                 (hideSubclasses0 == false) &&
                 (hideInheritedNodes0 == false) &&
                 (hideDiagram0 == false))
          FullDiagram
        else
          AnnotationDiagramFilter(
            hideDiagram = hideDiagram0,
            hideIncomingImplicits = hideIncomingImplicits0,
            hideOutgoingImplicits = hideOutgoingImplicits0,
            hideSuperclasses = hideSuperclasses0,
            hideSubclasses = hideSubclasses0,
            hideInheritedNodes = hideInheritedNodes0,
            hideNodesFilter = hideNodesFilter0,
            hideEdgesFilter = hideEdgesFilter0)

      if (settings.docDiagramsDebug && result != NoDiagramAtAll && result != FullDiagram)
        settings.printMsg(template.kind + " " + template.qualifiedName + " filter: " + result)
      tFilter += System.currentTimeMillis

      result
  }
}

Other Scala source code examples

Here is a short list of links related to this Scala DiagramDirectiveParser.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.