src/Tools/Graphview/src/mutator.scala
author wenzelm
Mon, 08 Oct 2012 23:29:07 +0200
changeset 49737 dd6fc7c9504a
parent 49565 ea4308b7ef0f
child 49743 31bfe82e9220
permissions -rw-r--r--
avoid somewhat heavy rebuilding of SortedMap via map where plain iteration is sufficient; more elementary level_list: avoid stack overflow; prefer elementary graph operations; tuned;

/*  Title:      Tools/Graphview/src/mutator.scala
    Author:     Markus Kaiser, TU Muenchen

Filters and add-operations on graphs.
*/

package isabelle.graphview


import isabelle._

import java.awt.Color
import scala.collection.immutable.SortedSet


trait Mutator
{
  val name: String
  val description: String
  def mutate(complete: Model.Graph, sub: Model.Graph): Model.Graph

  override def toString() = name
}

trait Filter extends Mutator
{
  def mutate(complete: Model.Graph, sub: Model.Graph) = filter(sub)
  def filter(sub: Model.Graph) : Model.Graph
}

object Mutators {
  type Mutator_Markup = (Boolean, Color, Mutator)
  
  val Enabled = true
  val Disabled = false
  
  def create(m: Mutator): Mutator_Markup =
    (Mutators.Enabled, Parameters.Colors.next, m)

  class Graph_Filter(val name: String, val description: String,
    pred: Model.Graph => Model.Graph)
  extends Filter
  {
    def filter(sub: Model.Graph) : Model.Graph = pred(sub)
  }

  class Graph_Mutator(val name: String, val description: String,
    pred: (Model.Graph, Model.Graph) => Model.Graph)
  extends Mutator
  {
    def mutate(complete: Model.Graph, sub: Model.Graph): Model.Graph =
          pred(complete, sub)
  }

  class Node_Filter(name: String, description: String,
    pred: (Model.Graph, String) => Boolean)
    extends Graph_Filter (

    name,
    description,
    g => g.restrict(pred(g, _))
  )

  class Edge_Filter(name: String, description: String,
    pred: (Model.Graph, String, String) => Boolean)
    extends Graph_Filter (

    name,
    description,
    g => (g /: g.dest) {
      case (graph, ((from, _), tos)) => {
        (graph /: tos) {
          (gr, to) => if (pred(gr, from, to)) gr
                      else gr.del_edge(from, to)
        }
      }
    }
  )

  class Node_Family_Filter(name: String, description: String,
      reverse: Boolean, parents: Boolean, children: Boolean,
      pred: (Model.Graph, String) => Boolean)
    extends Node_Filter(

    name,
    description,
    (g, k) => reverse != (
      pred(g, k) ||
      (parents && g.all_preds(List(k)).exists(pred(g, _))) ||
      (children && g.all_succs(List(k)).exists(pred(g, _)))
    )
  )  
  
  case class Identity()
    extends Graph_Filter(

    "Identity",
    "Does not change the graph.",
    g => g
  )

  case class Node_Expression(regex: String,
    reverse: Boolean, parents: Boolean, children: Boolean)
    extends Node_Family_Filter(

    "Filter by Name",
    "Only shows or hides all nodes with any family member's name matching " +
    "a regex.",
    reverse,
    parents,
    children,
    (g, k) => (regex.r findFirstIn k).isDefined
  )

  case class Node_List(list: List[String],
    reverse: Boolean, parents: Boolean, children: Boolean)
    extends Node_Family_Filter(

    "Filter by Name List",
    "Only shows or hides all nodes with any family member's name matching " +
    "any string in a comma separated list.",
    reverse,
    parents,
    children,
    (g, k) => list.exists(_ == k)
  )

  case class Edge_Endpoints(source: String, dest: String)
    extends Edge_Filter(

    "Hide edge",
    "Hides the edge whose endpoints match strings.",
    (g, s, d) => !(s == source && d == dest)
  )

  case class Edge_Transitive()
    extends Edge_Filter(

    "Hide transitive edges",
    "Hides all transitive edges.",
    (g, s, d) => {
      !g.imm_succs(s).filter(_ != d)  // FIXME iterator
      .exists(p => !(g.irreducible_paths(p, d).isEmpty))
    }
  )

  private def add_node_group(from: Model.Graph, to: Model.Graph,
    keys: List[String]) = {
    
    // Add Nodes
    val with_nodes = 
      (to /: keys) {
        (graph, key) => graph.default_node(key, from.get_node(key))
      }
    
    // Add Edges
    (with_nodes /: keys) {
      (gv, key) => {
        def add_edges(g: Model.Graph, keys: SortedSet[String], succs: Boolean) =
          (g /: keys) {
            (graph, end) => {
              if (!graph.keys.contains(end)) graph
              else {
                if (succs) graph.add_edge(key, end)
                else graph.add_edge(end, key)
              }
            }
          }

        
        add_edges(
          add_edges(gv, from.imm_preds(key), false),
          from.imm_succs(key), true
        )
      }
    }
  }  
  
  case class Add_Node_Expression(regex: String)
    extends Graph_Mutator(

    "Add by name",
    "Adds every node whose name matches the regex. " +
    "Adds all relevant edges.",
    (complete, sub) => {
      add_node_group(complete, sub, complete.keys.filter(
          k => (regex.r findFirstIn k).isDefined
        ).toList)
    }
  )
  
  case class Add_Transitive_Closure(parents: Boolean, children: Boolean)
    extends Graph_Mutator(

    "Add transitive closure",
    "Adds all family members of all current nodes.",
    (complete, sub) => {     
      val withparents = 
        if (parents)
          add_node_group(complete, sub, complete.all_preds(sub.keys.toList))
        else
          sub
      
      if (children)
        add_node_group(complete, withparents,
                       complete.all_succs(sub.keys.toList))
      else 
        withparents
    }
  )
}