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

swing.GraphicalDebugger.scala Maven / Gradle / Ivy

package subscript.swing

import java.awt.{Font, BasicStroke, Stroke, Color => AWTColor}
import java.awt.geom.AffineTransform
import java.util.concurrent.Executors

import javax.swing.JList

import scala.collection.mutable.ListBuffer
import scala.swing._

// import subscript.swing.Scripts._
import subscript.DSL._
import subscript.vm._
import subscript.vm.executor._
import subscript.vm.model.template._
import subscript.vm.model.template.concrete._
import subscript.vm.model.callgraph._

/*
 * Graphical script debugger
 * 
 * Operation mode 1: pass the class to be debugged as parameter from the command line
 * 
 *   execute the main method of GraphicalDebugger 
 *   with optional first argument: -s "text to be displayed in description text field"
 *   and then argument: the package+class name of the object to be debugged
 *   and later arguments: the arguments to be passed to the debugged object
 * 
 * Operation mode 2: pass the debugger as an argument to the subscript.vm._execute method:
 * 
 * 	  val debugger = new GraphicalDebugger
 *    _execute(scriptDef, debugger, executor)
 */

trait GraphicalDebugger extends MsgListener {

  def traceLevel = 2
  def otherTraceLevel = 1
  def trace(s: => Any) = if (traceLevel>0) {println(s); Console.flush}
  
  private var _otherScriptExecutor: ScriptExecutor[_] = null
  var vmThread: Thread = null
  
  def otherScriptExecutor = _otherScriptExecutor
  override def attach(p: MsgPublisher) {
    super.attach(p)
    _otherScriptExecutor = p.asInstanceOf[ScriptExecutor[_]]
    _otherScriptExecutor.traceLevel = otherTraceLevel
    _otherScriptExecutor.name = "otherScriptExecutor"
  }
  
  def main(args: Array[String]): Unit = {
    var lArgs = args
    if (lArgs.isEmpty) return
    ScriptExecutorFactory.addScriptDebugger(this)
    top.visible = true
    
    lArgs.head match {
      case "-s" => lArgs = lArgs.tail; if (lArgs.isEmpty) return; descriptionTF.text = lArgs.head
                   lArgs = lArgs.tail; if (lArgs.isEmpty) return
      case _ =>
    }
    vmThread = new Thread{override def run={
      live
      quit
    }}
    vmThread.start()
    
    val className = lArgs.head
    try {
      val c = Class.forName(className) // TBD: should be a swing application
      val m = c.getMethod("main", classOf[Array[String]])
      println("debugger: "+this.getClass.getCanonicalName+" on: "+className)
      m.invoke(null, lArgs.tail)
    }
    catch {
      case e: ClassNotFoundException => println("Could not find class "+className)
      case other: Throwable => other.printStackTrace
    }
  }

  def live: Unit
  def quit: Unit

  var messageBeingHandled = false
  var currentMessage: CallGraphMessage = null
  
  def interestingContinuationInternals(c: Continuation): List[String] = {
     var ss: List[String] = Nil
     if (c != null) {
          if ( c.success       != null) ss ::= "Success"
          if (!c.aaHappeneds  .isEmpty) ss ::= "AA Happened"
          if (!c.deactivations.isEmpty) ss ::= "Deactivations"
          if ( c.activation    != null) ss ::= "Activation"
     }
     ss
  }
  
  val exitButton = new Button("Exit"  ) {enabled = false; defaultCapable = false; focusable=false }
  val stepButton = new Button("Step"  ) {enabled = false}
  
  //"Activation  "
  //"Deactivation"
  //"Continuation"
  //"AAHappened     "
  //"Success     "
  //"Break       "
  //"Exclude     "
  
  
  val checkBox_step_Activation     = new CheckBox {text = "Act" ; selected = true}
  val checkBox_step_Deactivation   = new CheckBox {text = "Dea" ; selected = true}
  val checkBox_step_CFToBeExecuted = new CheckBox {text = "AAT" ; selected = true}
  val checkBox_step_Continuation   = new CheckBox {text = "Cnt" ; selected = true}
  val checkBox_step_AAHappened     = new CheckBox {text = "AAH" ; selected = true}
  val checkBox_step_Success        = new CheckBox {text = "Scs" ; selected = true}
  val checkBox_step_Break          = new CheckBox {text = "Brk" ; selected = true}
  val checkBox_step_Exclude        = new CheckBox {text = "Exc" ; selected = true}
  val checkBox_step_Wait           = new CheckBox {text = "Idle"; selected = true}

  val checkBox_log_Activation      = new CheckBox {text = "Act" ; selected = true}
  val checkBox_log_Deactivation    = new CheckBox {text = "Dea" ; selected = true}
  val checkBox_log_CFToBeExecuted  = new CheckBox {text = "AAT" ; selected = true}
  val checkBox_log_Continuation    = new CheckBox {text = "Cnt" ; selected = true}
  val checkBox_log_AAHappened      = new CheckBox {text = "AAH" ; selected = true}
  val checkBox_log_Success         = new CheckBox {text = "Scs" ; selected = true}
  val checkBox_log_Break           = new CheckBox {text = "Brk" ; selected = true}
  val checkBox_log_Exclude         = new CheckBox {text = "Exc" ; selected = true}
  val checkBox_log_Wait            = new CheckBox {text = "Idle"; selected = true}

  val buttonsPanel = new BoxPanel(Orientation.Vertical) {
    contents += new Label("Step:")
    contents += checkBox_step_Activation  
    contents += checkBox_step_Deactivation
    contents += checkBox_step_CFToBeExecuted  
    contents += checkBox_step_Continuation
    contents += checkBox_step_AAHappened   
    contents += checkBox_step_Success     
    contents += checkBox_step_Break       
    contents += checkBox_step_Exclude     
    contents += checkBox_step_Wait
    contents += new Label("  ---  ")
    contents += new Label("Log:")
    contents += checkBox_log_Activation  
    contents += checkBox_log_Deactivation
    contents += checkBox_log_CFToBeExecuted  
    contents += checkBox_log_Continuation
    contents += checkBox_log_AAHappened     
    contents += checkBox_log_Success     
    contents += checkBox_log_Break       
    contents += checkBox_log_Exclude     
    contents += checkBox_log_Wait
  }
  val callGraphPanel = new Panel {
    background = AWTColor.white
    preferredSize  = new Dimension(3000,2000)
    override def paint(g: Graphics2D) {
        g.setColor(AWTColor.white)
        g.fillRect(0, 0, size.width, size.height)
        try onPaintCallGraph(g)
        catch {case ex:Exception => ex.printStackTrace()}
    }
  }
  val templateTreesPanel = new Panel {
    background = AWTColor.white
    preferredSize  = new Dimension(4600,900)
    override def paint(g: Graphics2D) {
        g.setColor(AWTColor.white)
        g.fillRect(0, 0, size.width, size.height)
        onPaintTemplateTrees(g)
    }
  }
  val fixedWidthFont = new Font("Monaco", Font.PLAIN, 14)
  val currentMsgFont = new Font("Monaco", Font.BOLD , 14)
 
  val     normalFont = new Font("Arial" , Font.BOLD, 16)
  val      smallFont = new Font("Arial" , Font.BOLD, 13)
  val   normalStroke = new BasicStroke(1)
  val      fatStroke = new BasicStroke(3)
  
  val lightOrange = new AWTColor(255, 238, 220)
  val lightGreen  = new AWTColor(220, 255, 220)
  val lightBlue   = new AWTColor(220, 220, 255)
  val lightRed    = new AWTColor(255, 220, 220)
  val lightPurple = new AWTColor(255, 220, 255)
  
  def fillColor(n: CallGraphNode, defaultColor: AWTColor, allowOverride: Boolean) = 
         if (allowOverride && n.isExecuting ) lightPurple  
    else if (allowOverride && n.isActionBusy) lightRed 
    else                                      defaultColor

  
  def drawStringCentered(g: Graphics2D, s: String, cx: Int, cy: Int) {
    val sw = g.getFontMetrics.stringWidth(s)
    val sh = g.getFontMetrics.getHeight
    g.drawString(s, cx-sw/2, cy+sh/2)
  }
  def drawStringTopLeft(g: Graphics2D, s: String, x: Int, y: Int) {
    val sh = g.getFontMetrics.getHeight
    g.drawString(s, x, y+sh/2)
  }
  def drawStringTopRight(g: Graphics2D, s: String, x: Int, y: Int) {
    val sw = g.getFontMetrics.stringWidth(s)
    val sh = g.getFontMetrics.getHeight
    g.drawString(s, x-sw, y+sh/2)
  }
  def emphasize_g(g: Graphics2D, doIt: Boolean) {
    if (doIt) {
      g.setColor(AWTColor.red)
      g.setStroke(fatStroke)
    }
    else {
      g.setColor(AWTColor.black)
      g.setStroke(normalStroke)
    }
  }
  def onPaintTemplateTrees(g: Graphics2D) {
    val GRID_W  =  50
	val GRID_H  =  33
	val RATIO_W = 0.8
	val RATIO_H = 0.67
	val BOX_W   = (GRID_W * RATIO_W).toInt
	val BOX_H   = (GRID_H * RATIO_H).toInt
    val hOffset = (GRID_W - BOX_W)/2
    val vOffset = (GRID_H - BOX_H)/2
  
    def emphasize(doIt: Boolean) {emphasize_g(g, doIt)}

    def getScriptTemplates: List[T_script] = {
      val lb = new ListBuffer[T_script]
      def getScriptTemplates(n: CallGraphNode): Unit = {
        n match {case ns: Script[_] => if (!lb.exists(_.name.name==ns.template.name.name)) lb += ns.template case _ =>}
        n match {case pn: CallGraphNode => pn.forEachChild{getScriptTemplates(_)} case _ =>}
      } 
      getScriptTemplates(rootNode)
      lb.toList
    }
    def drawEdge(p: TemplateNode, pwx: Double, pwy: Int, 
                 c: TemplateNode, cwx: Double, cwy: Int): Unit = {
        
        val pHCenter = (pwx*GRID_W).toInt + BOX_W/2 + hOffset
        val pBottom  =  pwy*GRID_H        + BOX_H   + vOffset
        val cHCenter = (cwx*GRID_W).toInt + BOX_W/2 + hOffset
        val cTop     =  cwy*GRID_H                  + vOffset
        
        val x1       = pHCenter
        val y1       = pBottom
        val x2       = cHCenter
        val y2       = cTop
        
        g.drawLine(x1, y1, x2, y2)
      }
    def drawTemplateTree(t: TemplateNode, xGrid: Double, yGrid: Int): (Double, Double) = {
        var resultW = 0d
        var childHCs = new ListBuffer[Double]
	    if (t.children.isEmpty) resultW = 1d
	    else {
             t.children.foreach{ ct =>
	            val (childW, childHC) = drawTemplateTree(ct, xGrid+resultW, yGrid+1)
	            resultW  += childW
	            childHCs += childHC
              }
	    }
        val thisX   = xGrid+(resultW-1)/2 
        
        val s        = t.toString()
        val sw       = g.getFontMetrics.stringWidth(s)
        val boxWidth = math.max(sw + 4, BOX_W) // ensure the box fits the string
        val hOffset1 = (GRID_W - boxWidth)/2
        val boxLeft  = (thisX*GRID_W).toInt+hOffset1
        val boxTop   = yGrid*GRID_H+vOffset
        val hCenter  = boxLeft + boxWidth/2
        val vCenter  = boxTop  + BOX_H/2

        val r = new Rectangle(boxLeft, boxTop, boxWidth, BOX_H)
        //val n = if  (currentMessage==null||currentMessage.node==null) null 
        //        else currentMessage.node.asInstanceOf[CallGraphNode]    strange NPE's
        val n1 = if  (currentMessage==null) null 
                 else currentMessage.node
        val n = n1.asInstanceOf[CallGraphNode]
        val nameFont = t match {
          case _:T_call[_] | _:T_script => smallFont
          case _        => normalFont
        }

        // this check goes a bit wrong, resulting in too many template nodes being highlighted occasionally.
        // The problem is that almost "equal" templates are still different per node, and we don't want to draw them all.
        // Sometime a solution will be found
        val isCurrentTemplate = currentMessage            != null    && 
                                n                         != null    && 
                                n.template                != null    &&
                                (n.template eq t)
//                                ( n.template.root == null   && t.root == null
//                                ||n.template.root.name      == t.root.name   ) && 
//                                n.template.owner          != null             &&
//                                n.template.owner.getClass == t.owner.getClass && 
//                                n.template.indexInScript  == t.indexInScript
                                
        g.setColor(fillColor(n, lightOrange, isCurrentTemplate)) 
        g fill r
        emphasize(isCurrentTemplate)
        g draw r
        emphasize (false)
        g.setFont(nameFont)
        drawStringCentered(g, s, hCenter, vCenter-3)
        (t.children zip childHCs).foreach{ c_hc: (TemplateNode, Double) =>
          drawEdge(t, thisX, yGrid, c_hc._1, c_hc._2, yGrid+1)
        }
        (resultW, thisX)
    }
    
    if (otherScriptExecutor==null) return
    g.setFont(normalFont)
    var currentXGrid = 0d // drawn width of template trees so far, in Grid coordinates
    getScriptTemplates.foreach { t => currentXGrid += drawTemplateTree(t, currentXGrid, 0)._1}
  }
  def onPaintCallGraph(g: Graphics2D) {
    val GRID_W  =  90
	val GRID_H  =  43
	val RATIO_W = 0.75
	val RATIO_H = 0.6
	val BOX_W   = (GRID_W * RATIO_W).toInt
	val BOX_H   = (GRID_H * RATIO_H).toInt
    val hOffset = (GRID_W - BOX_W)/2
    val vOffset = (GRID_H - BOX_H)/2

      def emphasize(doIt: Boolean) {emphasize_g(g, doIt)}
      
      g.setFont(normalFont)

      def drawArrow(x1: Int, y1: Int, x2: Int, y2: Int, s: String) {
        val dx    = x2 - x1
        val dy    = y2 - y1
        val angle = math.atan2(dy, dx)
        val len   = math.sqrt(dx*dx + dy*dy).intValue
        val ARROW_HEAD_W = 5
        val ARROW_HEAD_L = 15
        
        emphasize(s != null)
        if (s != null) {
          g.setFont(normalFont)
          drawStringTopLeft(g, s, x1 + dx/2 + 9, y1 + dy/2 - 2)
        }
        val oldTransform = g.getTransform()
        val at = oldTransform.clone.asInstanceOf[AffineTransform]
        at.concatenate(AffineTransform.getTranslateInstance(x1, y1))
        at.concatenate(AffineTransform.getRotateInstance(angle))
        g.setTransform(at)

        // Draw horizontal arrow starting in (0, 0)
        g.drawLine(0, 0, len, 0)
        
        if (s != null) {
          g.fillPolygon(Array(len, len-ARROW_HEAD_L, len-ARROW_HEAD_L, len),
                        Array(  0,    -ARROW_HEAD_W,     ARROW_HEAD_W,   0), 4)
        }
        g.setTransform(oldTransform)
        emphasize(false)
      }
      def getBreakText(a: ActivationMode.ActivationModeType): String = {
        a match {
          case ActivationMode.Optional => "Optional Break"
          case _ => "Break"
        }
      }
      def drawEdge(p: CallGraphNode, pwx: Double, pwy: Int, 
                   c: CallGraphNode, cwx: Double, cwy: Int): Unit = {
        
        val pHCenter = (pwx*GRID_W).toInt + BOX_W/2 + hOffset
        val pBottom  =  pwy*GRID_H        + BOX_H   + vOffset
        val cHCenter = (cwx*GRID_W).toInt + BOX_W/2 + hOffset
        val cTop     =  cwy*GRID_H                  + vOffset
        
        val x1       = pHCenter
        val y1       = pBottom
        val x2       = cHCenter
        val y2       = cTop
        
        val text:String = if (currentMessage==null) null else
          currentMessage match { // node.index is not checked by node.equals!!!!
            case AAHappened(mp,mc,mode) if(p.index==mp.index&&c.index==mc.index)  => "AA Happened"
            case SuccessMsg(mp,null)                                              =>  null
            case SuccessMsg(mp,mc)      if (p.index==mp.index&&c.index==mc.index) => "Success"
            case Break     (mp,mc,mode) if (p.index==mp.index&&c.index==mc.index) => getBreakText(mode)
            case Exclude   (mp,mc)      if (p.index==mp.index&&c.index==mc.index) => "Exclude"
            case _                                                                => null
          }
        
        val doDownwards = currentMessage.isInstanceOf[Exclude]
        if (doDownwards) drawArrow(x1, y1, x2, y2, text)
        else             drawArrow(x2, y2, x1, y1, text)
        
        g.setStroke(normalStroke)
        g.setColor (AWTColor.black)
      }
      def drawContinuationTexts(n: CallGraphNode, boxRight: Int, boxTop: Int) = {
        val x = boxRight + 3
        var y = boxTop
        n match {
          case nn: N_n_ary_op if (nn.continuation != null) =>     
            val fontMetrics = g.getFontMetrics
            interestingContinuationInternals(nn.continuation).foreach{s: String=>drawStringTopLeft(g, s, x, y); y += fontMetrics.getHeight - 2}
          case _ => if (currentMessage!=null&¤tMessage.node==n) currentMessage match {
            case s: SuccessMsg if (s.child==null) => drawStringTopLeft(g, "Success"    , x, y) 
            case a: AAHappened if (a.child==null) => drawStringTopLeft(g, "AA Happened", x, y) 
            case _ => 
          }
        }
      }
	  def drawTree[T <: TemplateNode](n: CallGraphNode, xGrid: Double, yGrid: Int): (Double, Double) = {
        var resultW = 0d // drawn width of this subtree
        var childHCs = new ListBuffer[Double]
        
        val isCurrentNode = currentMessage != null && currentMessage.node.asInstanceOf[CallGraphNode].index == n.index
	    n match {
	      case p:CallGraphNode => 
	        val pcl=p.children.length
	        if (pcl==0) {
	          resultW = 1
	        }
	        else {
              p.children.foreach{ c =>
	            val (childW, childHC) = drawTree(c, xGrid+resultW, yGrid+1)
	            resultW  += childW
	            childHCs += childHC
              }
	        }
	      case _ => resultW = 1
	    }
        val thisX     = xGrid+(resultW-1)/2 
        val boxLeft   = (thisX*GRID_W).toInt+hOffset
        val boxTop    = yGrid*GRID_H+vOffset
        val boxRight  = boxLeft + BOX_W
        val boxBottom = boxTop  + BOX_H
        val hCenter   = boxLeft + BOX_W/2
        val vCenter   = boxTop  + BOX_H/2
        
        val s: String = n match {
          case ns: Script[_]  => ns.template.name.name
          case no: N_n_ary_op => no.template.kind + (if (no.isIteration) " ..." else "")
          case _              => n .template.kind
        }
        val nameFont = n match {
          case _: Script[_] | _: N_call[_] => smallFont
          case _                           => normalFont
        }
        
        val r = new Rectangle(boxLeft, boxTop, BOX_W, BOX_H)
        g.setColor(fillColor(n, lightGreen, true)) 
        g fill r
        emphasize(isCurrentNode)
        g draw r 
        if (isCurrentNode) {
          currentMessage match {
            case dm: Deactivation =>
              n match {
                case nn: N_n_ary_op if(dm.child!=null) => // will get Continuation
                case pn: CallGraphNode if (pn.children.length>0) =>
                case _ => // strike through with an "X"
                  g.drawLine(boxLeft, boxTop   , boxRight, boxBottom)
                  g.drawLine(boxLeft, boxBottom, boxRight, boxTop   )
              }
            case _ =>
          }
        }
        emphasize(false)
        g.setFont(smallFont)
        drawContinuationTexts(n, boxRight, boxTop)
        drawStringTopLeft (g, n.index.toString, boxLeft+2, boxTop+5)
        if (n.hasSuccess) drawStringTopRight(g, "S", boxRight-1, boxTop+5)
        g.setFont(nameFont)
        drawStringCentered(g, s, hCenter, vCenter)
	    n match {
          case nn:  N_n_ary_op => 
            if (nn.activationMode!=ActivationMode.Active) {
              val s = if (nn.activationMode!=ActivationMode.Inactive) "-" else "."
              g.setFont(smallFont)
              drawStringTopLeft (g, s, boxRight-7, boxTop+5)
            }
	      case _ =>
        }
	    n match {
	      case p:CallGraphNode if !p.children.isEmpty => 
	        (p.children zip childHCs).foreach{ c_hc: (CallGraphNode, Double) =>
	          drawEdge(n, thisX, yGrid, c_hc._1, c_hc._2, yGrid+1)
	        }
	      case _ =>
	    }
        (resultW, thisX)
	  }
      if (otherScriptExecutor!=null) drawTree(rootNode, 0, 0)
  }
  
  val maxLogListMsgs    = 5000
  val logListMsgsCleanups = 1000
  val msgLogListModel   = new javax.swing.DefaultListModel[String]
  val msgQueueListModel = new javax.swing.DefaultListModel[CallGraphMessage]
  val currentMessageTF  = new TextField {
    //preferredSize       = new Dimension(400,24)
    //minimumSize         = preferredSize
    editable            = false
    font                = currentMsgFont
    background          = lightBlue
    horizontalAlignment = scala.swing.Alignment.Left
  }
  val msgLogList        = new ListBuffer[String]
  val msgQueueList      = new ListBuffer[String]
  val msgLogListView    = new ListView(msgLogList) {
    font                = fixedWidthFont
    peer.asInstanceOf[JList[String]].setModel(msgLogListModel)   // Compiler throws an exception without a cast
  }
  val msgQueueListView  = new ListView(msgQueueList) {
    font                = fixedWidthFont
    peer.asInstanceOf[JList[CallGraphMessage]].setModel(msgQueueListModel)
  }
  val msgLogListViewScrollPane   = new ScrollPane
  {
  	contents                     = msgLogListView
  	verticalScrollBarPolicy      = ScrollPane.BarPolicy.Always
  }
  val msgQueueListViewScrollPane = new ScrollPane
  {
  	contents                     = msgQueueListView
  	verticalScrollBarPolicy      = ScrollPane.BarPolicy.Always
  }
  val borderPanelMsgs = new BorderPanel {
    add(msgLogListViewScrollPane, BorderPanel.Position.Center)
    add(currentMessageTF        , BorderPanel.Position.South)
  }
  
  var splitPaneMain: SplitPane = null
  val doTemplateTreeTopLeft = true
  
  if (doTemplateTreeTopLeft) {  
    val splitPaneMsgs      = new SplitPane(scala.swing.Orientation.Horizontal, borderPanelMsgs,       msgQueueListViewScrollPane) {dividerLocation  = 250}
    val splitPaneTreesMsgs = new SplitPane(scala.swing.Orientation.Horizontal, new ScrollPane(templateTreesPanel), splitPaneMsgs) {dividerLocation  = 250}
    val splitPaneLeft      = new SplitPane(scala.swing.Orientation.Vertical  , buttonsPanel                 , splitPaneTreesMsgs) {dividerLocation  =  60}
    splitPaneMain          = new SplitPane(scala.swing.Orientation.Vertical  ,   splitPaneLeft,   new ScrollPane(callGraphPanel)) {dividerLocation  = 400}
  }
  else {  
    val splitPaneGraphs = new SplitPane(scala.swing.Orientation.Horizontal, new ScrollPane(templateTreesPanel), 
                                                                            new ScrollPane(callGraphPanel)              ) {dividerLocation  = 178}
    val splitPaneMsgs   = new SplitPane(scala.swing.Orientation.Horizontal, borderPanelMsgs,  msgQueueListViewScrollPane) {dividerLocation  = 350}
    splitPaneMain       = new SplitPane(scala.swing.Orientation.Vertical,     splitPaneMsgs,             splitPaneGraphs) {dividerLocation  = 240}
  } 
  val descriptionTF     = new TextField {
    preferredSize       = new Dimension(400,24)
    editable            = false
    font                = fixedWidthFont //normalFont
  }
  val autoCheckBox      = new CheckBox {
    text                = "Auto"
  //selected            = true
  }
  val speedSlider       = new Slider {
    min                 =   0
    max                 =  10
    value               =   5
  }
  
  val top               = new Frame {
    title               = "Subscript Graphical Debugger"
    location            = new Point    (0,0)
    preferredSize       = new Dimension(900,700)
    contents            = new BorderPanel {
      add(new FlowPanel(descriptionTF, stepButton, autoCheckBox, speedSlider, exitButton), BorderPanel.Position.North) 
      add(splitPaneMain, BorderPanel.Position.Center)
    }
  }
  
  def sleep(duration_ms: Long) = try {Thread.sleep(duration_ms)} catch {case e: InterruptedException => println("sleep interrupted")}
  def confirmExit: Boolean = Dialog.showConfirmation(exitButton, "Are you sure?", "About to exit")==Dialog.Result.Yes
  
  def shouldStep: Boolean =
    currentMessage match {
      case Activation(_)       => checkBox_step_Activation    .selected
      case Deactivation(_,_,_) => checkBox_step_Deactivation  .selected
      case CFToBeExecuted(_)   => checkBox_step_CFToBeExecuted.selected
      case AAHappened(_,_,_)   => checkBox_step_AAHappened    .selected
      case SuccessMsg(_,_)     => checkBox_step_Success       .selected
      case Break(_,_,_)        => checkBox_step_Break         .selected
      case Exclude(_,_)        => checkBox_step_Exclude       .selected
      case c:Continuation      => checkBox_step_Continuation  .selected && !interestingContinuationInternals(c).isEmpty
      case _                   => false    
    }
  
  def MAX_STEP_DELAY_SEC = 5

  def stepSleep_ms = (math.pow((speedSlider.max-speedSlider.value) / speedSlider.max.toDouble, 3) * MAX_STEP_DELAY_SEC * 1000).intValue
    
  def waitForStepTimeout = {
    try {
      val sleepPart_ms = 10
      var slept_ms     = 0
      while(slept_ms }
//    trace(f"waitForStepTimeout done")
  }
  def logMessage_GUIThread(m: String, msg: CallGraphMessage) {
    
      if (msg match {
        case Activation(_)       => checkBox_log_Activation    .selected
        case Deactivation(_,_,_) => checkBox_log_Deactivation  .selected
        case CFToBeExecuted(_)   => checkBox_log_CFToBeExecuted.selected
        case AAHappened(_,_,_)   => checkBox_log_AAHappened    .selected
        case SuccessMsg(_,_)     => checkBox_log_Success       .selected
        case Break(_,_,_)        => checkBox_log_Break         .selected
        case Exclude(_,_)        => checkBox_log_Exclude       .selected
        case c:Continuation      => checkBox_log_Continuation  .selected
        case _                   => false
      }) {    
        var runnable = new Runnable {
          def run(): Unit = {logMessage(m, msg)}
        }
        javax.swing.SwingUtilities.invokeLater(runnable)
      }
  }
  def logMessage(m: String, msg: CallGraphMessage) {
    if (msgLogListModel.size > maxLogListMsgs)
      msgLogListModel.removeRange(0, logListMsgsCleanups)
    msgLogListModel.addElement(m + " " + msg)
    msgLogListViewScrollPane.verticalScrollBar.value = msgLogListViewScrollPane.verticalScrollBar.maximum
    msgQueueListModel.clear
    
    // Sorting the list of scriptExecutor messages
    // before rendering
    val ord = otherScriptExecutor.msgQueue.ordering
    val orderedMessages = callGraphMessages.toList.sorted(ord).reverse
    orderedMessages.foreach(msgQueueListModel.addElement(_)) 
  }
  def updateDisplay = {
    var s = if (currentMessage==null) "." else currentMessage.toString
    if (s.length>50) s = s.substring(0, 50) + "..."
    currentMessageTF.text = s
    logMessage (">>", currentMessage)
    callGraphPanel    .repaint()
    templateTreesPanel.repaint()
  }
  def doesThisAllowToBeDebugged = false // overridden in GraphicalDebugger2
  val myScriptExecutor = ScriptExecutorFactory.createScriptExecutor[Any](doesThisAllowToBeDebugged)
  myScriptExecutor.name = "myScriptExecutor";


  var exitConfirmed = false 

  def kickExecutors = {kickExecutor(myScriptExecutor); kickExecutor(otherScriptExecutor)}
  def kickExecutor(executor: ScriptExecutor[_]) = {
    if (executor!=null) {                       //trace(f"kickExecutor start: $executor")
      executor.synchronized{executor.notify()}; //trace(f"kickExecutor   end: $executor")
    }  
  }
  
  object MessageStatusLock
  def messageBeingHandled(value: Boolean): Unit = {
//    trace(f" messageBeingHandled($value%5s) start")
    MessageStatusLock.synchronized {
      //trace(f" messageBeingHandled($value%5s) synchronized")
      // maybe needed because sometimes (=race condition) the other script executor would stay waiting
      kickExecutor(otherScriptExecutor)
      messageBeingHandled = value
      MessageStatusLock.notifyAll()
    }
//    trace(f" messageBeingHandled($value%5s) end")
  }
  
  def awaitMessageBeingHandled(value: Boolean) = {
    //trace(f"awaitMsgBeingHandled($value%5s) start")
    MessageStatusLock.synchronized {
      //trace(f"awaitMsgBeingHandled($value%5s) wait start")
      while (messageBeingHandled != value) MessageStatusLock.wait()
    //trace(f"awaitMsgBeingHandled($value%5s) wait end")
    }
    //trace(f"awaitMsgBeingHandled($value%5s) end")
  }
    
  import scala.collection.JavaConversions._
  def dumpStacks = for ((thread,sts) <- mapAsScalaMap(Thread.getAllStackTraces)) {
     trace(thread)
     sts foreach {trace(_)}
     trace("")
   }
   def dumpExecutors = {dumpExecutor(myScriptExecutor); dumpExecutor(otherScriptExecutor);}
   def dumpExecutor(executor: ScriptExecutor[_]) = {
     if (traceLevel > 1) {
       trace(executor)
       trace("====================")
       val debugger = new SimpleScriptDebuggerClass
       debugger.attach(executor)
       debugger.traceLevel = 4
       debugger.traceTree
       debugger.traceMessages
       trace("")
     }
   }
   
/*  
  override def _live  = _script(this, 'live) {_par_or2(_seq(_threaded0{awaitMessageBeingHandled(true)}, 
                                                      _if0{shouldStep} (_par_or(_seq(_at{gui} (_tiny0{updateDisplay}), _stepCommand), 
                                                                               _if_else0{autoCheckBox.selected}(_threaded0{waitForStepTimeout}, _deadlock))), 
                                                      _normal0{messageBeingHandled=false}, 
                                                      _loop
                                                     ), 
                                                  _exitDebugger
                                                )}
  def   _stepCommand  = _script(this, 'stepCommand ) {_clicked(stepButton)}
  def   _exitCommand  = _script(this, 'exitCommand ) {_clicked(exitButton)} // windowClosing
  def   _exitDebugger = _script(this, 'exitDebugger) {_seq(  _exitCommand, _at{gui}(_while0{!confirmExit}))}
//def   _exitDebugger = _script('exitDebugger) {_seq(  _exitCommand, _at{gui}(_normal{exitConfirmed=confirmExit}), _while{!exitConfirmed})}
  
  */
  def callGraphMessages = otherScriptExecutor.msgQueue.collection
  def rootNode          = otherScriptExecutor.rootNode
  
  override def messageHandled(m: CallGraphMessage): Unit = {
    currentMessage = m
    messageBeingHandled(true) 
    awaitMessageBeingHandled(false)
    currentMessage = null
  }
  override def messageQueued      (m: CallGraphMessage                 ) = logMessage_GUIThread("++", m)
  override def messageDequeued    (m: CallGraphMessage                 ) = logMessage_GUIThread("--", m)
  override def messageContinuation(m: CallGraphMessage, c: Continuation) = logMessage_GUIThread("**", c)
  override def messageAwaiting: Unit = {
    trace(f"messageAwaiting")
    if (checkBox_log_Wait .selected) currentMessageTF.text = "Waiting..."
    if (checkBox_step_Wait.selected) callGraphPanel.repaint()
}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy