Forráskód Böngészése

Merge branch 'BetterStructure' of GraphDrawer/NodeShuffler into master

Eren B. Yilmaz 6 éve
szülő
commit
81efb90db4
47 módosított fájl, 2957 hozzáadás és 3034 törlés
  1. 1 0
      .classpath
  2. 9 3
      doc/chapter/4progress.tex
  3. BIN
      lib/guava-18.0-sources.jar
  4. BIN
      material/~$lec07-Nodes-Size.pptx
  5. 0 88
      src/animation/AlgorithmStage.java
  6. 49 32
      src/animation/AnimatedAlgorithm.java
  7. 0 14
      src/animation/BackwardAction.java
  8. 31 0
      src/animation/CodeLine.java
  9. 66 0
      src/animation/ControlFlow.java
  10. 184 0
      src/animation/Memory.java
  11. 89 8
      src/animation/PseudoCodeNode.java
  12. 229 0
      src/animation/PseudoCodeProcessor.java
  13. 51 0
      src/animation/StackFrame.java
  14. 151 426
      src/bk/BKNodePlacement.java
  15. 224 440
      src/bk/BlockCalc.java
  16. 200 438
      src/bk/Combine.java
  17. 148 526
      src/bk/Compaction.java
  18. 221 644
      src/bk/ConflictDetection.java
  19. 0 291
      src/bk/ExtremalLayoutCalc.java
  20. 30 0
      src/bk/LayoutType.java
  21. 180 0
      src/bk/PlaceBlock.java
  22. 65 0
      src/codelines/AbstractForLoop.java
  23. 74 0
      src/codelines/BackwardForEachLoop.java
  24. 12 0
      src/codelines/Comment.java
  25. 42 0
      src/codelines/DeclareVariable.java
  26. 72 0
      src/codelines/ForEachLoop.java
  27. 75 0
      src/codelines/ForLoop.java
  28. 39 0
      src/codelines/FunctionCall.java
  29. 54 0
      src/codelines/FunctionDefinition.java
  30. 45 0
      src/codelines/IfLoop.java
  31. 35 0
      src/codelines/SetVariable.java
  32. 57 0
      src/codelines/WhileLoop.java
  33. 1 1
      src/graph/InitializeNodePositions.java
  34. 6 1
      src/graph/LayeredEdge.java
  35. 1 1
      src/graph/LayeredGraphEdge.java
  36. 9 1
      src/graph/LayeredGraphNode.java
  37. 63 32
      src/graph/LayeredNode.java
  38. 1 1
      src/graph/RandomGraphGenerator.java
  39. 3 3
      src/graph/io/Reader.java
  40. 2 2
      src/lib/SweepCrossingMinimizer.java
  41. 23 4
      src/lib/TextLayoutHelper.java
  42. 2 2
      src/main/Main.java
  43. 1 1
      src/view/EdgeView.java
  44. 149 8
      src/view/MainView.java
  45. 40 1
      src/view/NodeView.java
  46. 183 0
      src/view/PseudoCodeLines.java
  47. 40 66
      src/view/PseudoCodeRenderer.java

+ 1 - 0
.classpath

@@ -2,6 +2,7 @@
 <classpath>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
 	<classpathentry kind="src" path="src"/>
+	<classpathentry kind="lib" path="lib/guava-18.0-sources.jar"/>
 	<classpathentry kind="lib" path="lib/java-json.jar"/>
 	<classpathentry kind="lib" path="lib/org.eclipse.elk.alg.common.compaction-0.3.0-sources.jar"/>
 	<classpathentry kind="lib" path="lib/org.eclipse.elk.alg.common.compaction-0.3.0.jar"/>

+ 9 - 3
doc/chapter/4progress.tex

@@ -22,7 +22,7 @@ The following features are either planned (\planned), under construction (\progr
     \begin{itemize}
         \item[\done] Drawing the nodes at their current position.
         \item[\done] Drawing the nodes in the color of their blocks.
-        \item[\done] Drawing a colored circle to show the class assignments.
+        \item[\progress] Drawing a colored background to show the class assignments.
         \item[\done] Drawing the edges just as plain straight lines.
         \item[\done] Drawing the conflicted edges in a different color.
         \item[\done] Drawing dummy nodes differently from other nodes.
@@ -34,12 +34,18 @@ The following features are either planned (\planned), under construction (\progr
         \item[\done] Showing pseudocode and the position where the algorithm currently is.
         \begin{itemize}
             \item[\done] Clicking on the pseudocode to set a breakpoint at (not \reserved{goto}) a specific location.
+            \item[\planned] Customizing the font size of the code.
         \end{itemize}
-        \item[\planned] Add a legend to explain the shapes and colors.
+        \item[\planned] Showing a legend to explain the shapes and colors.
+        \item[\done] Showing tooltips when hovering over code lines and nodes in the graph that show the state of the variables.
+        \item[\planned] Hovering the mose over a node to highlight it in all layouts.
     \end{itemize}
     \item[\done] Running the algorithm step by step manually.
     \item[\done] Running the algorithm step by step with configurable delay (\enquote{automatic execution}).
-    \item[\done] Running the algorithm only on the unfolded parts of the source code (\enquote{step overrun}).
+    \item[\done] Running the algorithm only on the expanded parts of the source code (\enquote{step overrun}).
+    \begin{itemize}
+        \item[\planned] Right clicking the code to expand or collapse all lines up to a specified level.
+    \end{itemize}
     \item[\done] Using debugger-like commands such as \enquote{step into}, \enquote{step over}, \enquote{step out}.
     \item[\done] Adding buttons and other graphical elements to support the user interface (low priority, see figure~\ref{fig:sketch}).
     Currently there is only keyboard input (cf.\ section~\ref{sec:userInterface}).

BIN
lib/guava-18.0-sources.jar


BIN
material/~$lec07-Nodes-Size.pptx


+ 0 - 88
src/animation/AlgorithmStage.java

@@ -1,88 +0,0 @@
-package animation;
-
-import javax.swing.JTree;
-
-import bk.BlockCalc;
-
-/**
- * Represents a specific stage of the algorithm.
- * Example calculating the blocks, see {@link BlockCalc}.
- * Each of those stages also has an associated {@link PseudoCodeNode}, i.e., a line of pseudocode.
- * 
- * @author kolja
- *
- */
-public interface AlgorithmStage {
-    /**
-     * Indicates whether the whole stage is finished.
-     * @author kolja
-     *
-     */
-    public static enum StageStatus
-    {
-        UNFINISHED,
-        BREAKPOINT,
-        FINISHED
-    }
-
-    /**
-     * Perform one atomic step of the algorithm. Stops at the end of the program.
-     * @return whether the whole stage is finished (afterwards).
-     * For example if all steps are finished, then {@code FINISHED} is returned.
-     */
-    public StageStatus forwardStep();
-
-    /**
-     * Perform steps until the next line of code on the same level of indentation as this line
-     * is reached. Stops at the end of the program.
-     * @return whether the whole stage is finished (afterwards).
-     * For example if all steps are finished, then {@code FINISHED} is returned.
-     */
-    public StageStatus forwardStepOver();
-
-    /**
-     * Perform steps until the next line of code on the level of indentation above this lines 
-     * level is reached. Stops at the end of the program.
-     * @return whether the whole stage is finished (afterwards).
-     * For example if all steps are finished, then {@code FINISHED} is returned.
-     */
-    public StageStatus forwardStepOut();
-    
-    /**
-     * Undo one atomic step of the algorithm. Stops at the beginning of the program.
-     * @return whether the whole stage is finished in backwards direction (afterwards).
-     * For example if all steps have been reverted, then {@code FINISHED} is returned.
-     */
-    public StageStatus backwardStep();
-
-    /**
-     * Perform backward steps until the previous line of code on the same level of indentation 
-     * as this line is reached. Stops at the end of the program.
-     * @return whether the whole stage is finished in backwards direction (afterwards).
-     * For example if all steps have been reverted, then {@code FINISHED} is returned.
-     */
-    public StageStatus backwardStepOver();
-
-
-    /**
-     * Perform backward steps until the previous line of code on the level of indentation above 
-     * this lines level is reached. Stops at the end of the program.
-     * @return whether the whole stage is finished in backwards direction (afterwards).
-     * For example if all steps have been reverted, then {@code FINISHED} is returned.
-     */
-    public StageStatus backwardStepOut();
-
-    /**
-     * Creates a {@link PseudoCodeNode}, i.e., a line of pseudocode that resembles this Stage.
-     * @param tree The {@link JTree} where the node should be inserted.
-     * @return The node.
-     */
-    public PseudoCodeNode createPseudocodeTree( JTree tree );
-    
-    /**
-     * Writes the state of the variables used in this stage to a pretty table.
-     * This table should also render nicely if interpreted as markdown. 
-     * @return the table
-     */
-    public String getDebugString();
-}

+ 49 - 32
src/animation/AnimatedAlgorithm.java

@@ -7,19 +7,23 @@ import javax.swing.JFrame;
 import javax.swing.JTree;
 import javax.swing.SwingUtilities;
 
+import animation.PseudoCodeProcessor.CodeStatus;
 import graph.LayeredGraphNode;
 
-public abstract class AnimatedAlgorithm extends Thread implements AlgorithmStage {
+public abstract class AnimatedAlgorithm extends Thread {
 
     protected AnimationController ac;
     protected LayeredGraphNode graph;
     private JFrame view;
+    protected PseudoCodeNode root;
+    protected PseudoCodeProcessor processor;
 
     public AnimatedAlgorithm( AnimationController controller, LayeredGraphNode graph, JFrame view )
     {
         this.ac = controller;
         this.graph = graph;
         this.view = view;
+        root = null;
     }
 
     private void update()
@@ -40,46 +44,59 @@ public abstract class AnimatedAlgorithm extends Thread implements AlgorithmStage
     {
         while( true ) // if this loop would end we could not undo steps any more
         {
+        	CodeStatus status = null;
             try {
-                switch( ac.getNextAction() )
+                if( processor != null )
                 {
-                case FORWARD:
-                    if( forwardStep() == StageStatus.FINISHED )
-                        ac.setContinuous( false );
-                    update();
-                    break;
-                case FORWARD_OUT:
-                    forwardStepOut();
-                    update();
-                    break;
-                case FORWARD_OVER:
-                    forwardStepOver();
-                    update();
-                    break;
-                case BACKWARD:
-                    if( backwardStep() == StageStatus.FINISHED )
-                        ac.setContinuous( false );
-                    update();
-                    break;
-                case BACKWARD_OUT:
-                    backwardStepOut();
-                    update();
-                    break;
-                case BACKWARD_OVER:
-                    backwardStepOver();
-                    update();
-                    break;
-                default:
-                    break;
+                    Action action = ac.getNextAction();
+                    graph.unselectGraph();
+                    switch( action )
+                    {
+                    case FORWARD:
+                    	status = processor.forwardStep();
+                        break;
+                    case FORWARD_OUT:
+                        status = processor.forwardStepOut();
+                        break;
+                    case FORWARD_OVER:
+                        status = processor.forwardStepOver();
+                        break;
+                    case BACKWARD:
+                        status = processor.backwardStep();
+                        break;
+                    case BACKWARD_OUT:
+                        status = processor.backwardStepOut();
+                        break;
+                    case BACKWARD_OVER:
+                        status = processor.backwardStepOver();
+                        break;
+                    }
                 }
+                else
+                    status = CodeStatus.FINISHED;
             } catch (InterruptedException e) {
                 e.printStackTrace();
                 return;
             }
-
+            update();
+            if( status == CodeStatus.FINISHED )
+            {
+        		ac.setContinuous( false );
+        		ac.setNextAction( null );
+            }
         }
     }
 
-    @Override
     public abstract PseudoCodeNode createPseudocodeTree( JTree tree );
+    
+    public PseudoCodeProcessor getProcessor() {
+        return processor;
+    }
+    
+    public String getDebugString()
+    {
+        if( processor == null )
+            return "";
+        return processor.getDebugOutput();
+    }
 }

+ 0 - 14
src/animation/BackwardAction.java

@@ -1,14 +0,0 @@
-package animation;
-
-/**
- * An action that reverts another Action.
- * For example this is used to go one step backwards in the node placement algorithm.
- * @author kolja
- *
- */
-public interface BackwardAction {
-    /**
-     * Undo another action.
-     */
-    public void reverse();
-}

+ 31 - 0
src/animation/CodeLine.java

@@ -0,0 +1,31 @@
+package animation;
+
+import java.util.Stack;
+
+public abstract class CodeLine {
+
+	protected interface BackwardAction
+	{
+		public void backward( Memory m );
+	}
+	
+	protected Stack<BackwardAction> actions;
+	protected int lineId;
+	private static int nextLineId = 0;
+	
+	public CodeLine()
+	{
+		synchronized( CodeLine.class ) {
+			lineId = nextLineId++;
+		}
+		actions = new Stack<BackwardAction>();
+	}
+	
+    public abstract ControlFlow runForward( Memory m );
+    
+    public void runBackward( Memory m )
+    {
+    	if( actions.size() != 0 )
+    	    actions.pop().backward( m );
+    }
+}

+ 66 - 0
src/animation/ControlFlow.java

@@ -0,0 +1,66 @@
+package animation;
+
+import animation.CodeLine.BackwardAction;
+
+public class ControlFlow {
+    public static final int STEP_INTO = 0;
+    public static final int STEP_OVER = 1;
+    public static final int CALL = 2;
+    
+    private int status;
+    private PseudoCodeNode function;
+    private PseudoCodeNode jumpBack;
+    private BackwardAction reverse;
+    
+    public ControlFlow( int status )
+    {
+        this.status = status;
+        function = null;
+        jumpBack = null;
+        reverse = null;
+    }
+    
+    public ControlFlow( PseudoCodeNode functionNode )
+    {
+    	status = CALL;
+    	function = functionNode;
+        jumpBack = null;
+        reverse = null;
+    }
+    
+    /*
+     * Package Private from here on
+     * This is wanted because the individual algorithms should not have access to these functions
+     */
+    
+    void setJumpBack( PseudoCodeNode jB )
+    {
+        jumpBack = jB;
+    }
+    
+    void setBackwardAction( BackwardAction bka )
+    {
+        reverse = bka;
+    }
+    
+    void backward( Memory m )
+    {
+        if( reverse != null )
+            reverse.backward( m );
+    }
+    
+    PseudoCodeNode getJumpBack()
+    {
+        return jumpBack;
+    }
+    
+    PseudoCodeNode getFunction()
+    {
+    	return function;
+    }
+    
+    int getStatus()
+    {
+        return status;
+    }
+}

+ 184 - 0
src/animation/Memory.java

@@ -0,0 +1,184 @@
+package animation;
+
+import java.util.Stack;
+
+import animation.StackFrame.FrameType;
+
+public class Memory {
+
+    public enum MemoryType
+    {
+        GLOBAL,
+        LOCAL,
+        COMPLETE_STACK
+    }
+    
+    public class ReadOnlyMemory
+    {
+        public <T> T read( String name, MemoryType type )
+        {
+            return Memory.this.read( name, type );
+        }
+        
+        public boolean isSomewhereDefined( String name, MemoryType type )
+        {
+            return Memory.this.isSomewhereDefined( name, type );
+        }
+        
+        public boolean isDefined( String name, MemoryType type )
+        {
+            return Memory.this.isDefined( name, type );
+        }
+    }
+    
+    private StackFrame global;
+    private Stack< StackFrame > stack;
+    
+    
+    public Memory()
+    {
+        stack = new Stack<StackFrame>();
+        global = new StackFrame( FrameType.FUNCTION );
+    }
+    
+    public synchronized ReadOnlyMemory createReadOnlyMemory()
+    {
+        return new ReadOnlyMemory();
+    }
+    
+    public synchronized int getSize() 
+    {
+        return stack.size();
+    }
+    
+    public synchronized void addFrame( StackFrame frame )
+    {
+        stack.push( frame );
+    }
+    
+    public synchronized StackFrame removeFrame()
+    {
+        return stack.pop();
+    }
+    
+    public synchronized <T> void declare( String name, T value, MemoryType type )
+    {
+        switch( type )
+        {
+        case GLOBAL:
+            this.global.declare( name, value );
+            break;
+        case COMPLETE_STACK:
+        case LOCAL:
+        	if( stack.size() == 0 )
+        		return;
+            stack.peek().declare( name, value );
+            break;
+        }
+    }
+    
+    public synchronized <T> void write( String name, T value, MemoryType type )
+    {
+        switch( type )
+        {
+        case GLOBAL:
+            this.global.set( name, value );
+            break;
+        case COMPLETE_STACK:
+        case LOCAL:
+        	int index = stack.size() - 1;
+        	while( index >= 0 ) {
+        	   StackFrame stackF = stack.get( index-- );
+        	   if( stackF.isDefined( name ) )
+        	   {
+        		   stackF.set( name, value );
+        		   return;
+        	   }
+        	   if( stackF.getType() == FrameType.FUNCTION )
+        		   break;
+        	}
+        }
+    }
+    
+    public synchronized <T> T read( String name, MemoryType type )
+    {
+        int index = stack.size() - 1;
+        switch( type )
+        {
+        case GLOBAL:
+            return this.global.get( name );
+        case LOCAL:
+            while( index >= 0 ) {
+                StackFrame stackF = stack.get( index-- );
+                if( stackF.isDefined( name ) )
+                    return stackF.get( name );
+                if( stackF.getType() == FrameType.FUNCTION )
+                    break;
+             }
+            break;
+        case COMPLETE_STACK:
+            while( index >= 0 ) {
+                StackFrame stackF = stack.get( index-- );
+                if( stackF.isDefined( name ) )
+                    return stackF.get( name );
+             }
+        }
+        return null;
+    }
+    
+    public synchronized boolean isSomewhereDefined( String name, MemoryType type )
+    {
+        int index = stack.size() - 1;
+        switch( type )
+        {
+        case GLOBAL:
+            return this.global.isDefined( name );
+        case LOCAL:
+            while( index >= 0 ) {
+                StackFrame stackF = stack.get( index-- );
+                if( stackF.isDefined( name ) )
+                    return true;
+                if( stackF.getType() == FrameType.FUNCTION )
+                    break;
+             }
+            break;
+        case COMPLETE_STACK:
+            while( index >= 0 ) {
+                StackFrame stackF = stack.get( index-- );
+                if( stackF.isDefined( name ) )
+                    return true;
+             }
+        }
+    	return false;
+    }
+    
+    public synchronized boolean isDefined( String name, MemoryType type )
+    {
+        switch( type )
+        {
+        case GLOBAL:
+            return this.global.isDefined( name );
+        case COMPLETE_STACK:
+        case LOCAL:
+            if( stack.size() == 0 )
+                return false;
+            return stack.peek().isDefined( name );
+        }
+    	return false;
+    }
+    
+    public synchronized void undeclare( String name, MemoryType type )
+    {
+        switch( type )
+        {
+        case GLOBAL:
+            this.global.undeclare( name );
+            break;
+        case COMPLETE_STACK:
+        case LOCAL:
+        	if( stack.size() == 0 )
+        		return;
+            stack.peek().undeclare( name );
+        }
+    }
+}

+ 89 - 8
src/animation/PseudoCodeNode.java

@@ -1,10 +1,15 @@
 package animation;
 
+import java.awt.Rectangle;
+
 import javax.swing.JTree;
+import javax.swing.SwingUtilities;
 import javax.swing.tree.DefaultMutableTreeNode;
 import javax.swing.tree.MutableTreeNode;
 import javax.swing.tree.TreePath;
 
+import lib.TextLayoutHelper;
+
 /**
  * represents a line of pseudocode
  * @author kolja
@@ -20,18 +25,31 @@ public class PseudoCodeNode extends DefaultMutableTreeNode {
     }
     
     private static final long serialVersionUID = 1L;
-    
-    private boolean selected;
+
+    private static int nextNodeId = 0;
+    private final int nodeId;
+    private AnimationController controller;
     private JTree tree;
+    private CodeLine code;
+    private boolean selected;
     private boolean breakPoint;
-    private AnimationController controller;
     
-    public PseudoCodeNode( String description, JTree tree )
+    public PseudoCodeNode( String description, String[] vars, JTree tree, CodeLine line )
     {
-        super( description );
+        super( TextLayoutHelper.setupPseudoCode( description, vars ) );
+        synchronized( PseudoCodeNode.class )
+        {
+        	nodeId = nextNodeId++;
+        }
         selected = false;
         this.tree = tree;
         breakPoint = false;
+        code = line;
+    }
+
+    public int getId()
+    {
+        return nodeId;
     }
     
     public void setController( AnimationController c )
@@ -88,6 +106,13 @@ public class PseudoCodeNode extends DefaultMutableTreeNode {
     	return false;
     }
     
+    private void expandToRoot()
+    {
+    	if( parent != null )
+    		((PseudoCodeNode)parent).expandToRoot();
+        tree.expandPath( new TreePath( this.getPath() ) );
+    }
+
     /**
      * highlight this line of pseudocode.
      * should be called when the line is entered, as it triggers breakpoints
@@ -101,13 +126,42 @@ public class PseudoCodeNode extends DefaultMutableTreeNode {
         this.selected = selected;
         if( selected )
         {
+            if( tree != null ) {
+                TreePath path = new TreePath( getPath() );
+                Rectangle bounds = tree.getPathBounds(path);
+                if( bounds!= null )
+                {
+                    bounds.height = (int) (tree.getVisibleRect().height - tree.getVisibleRect().getHeight() / 2);
+                    bounds.x = 0;
+                    SwingUtilities.invokeLater( new Runnable() {
+                        @Override
+                        public void run() {
+                            tree.scrollRectToVisible(bounds);
+                        }
+                    });
+                }
+            }
             if( controller == null || controller.getStepOption() != 1 || breakPoint )
-                tree.expandPath( new TreePath( this.getPath() ) );
+            {
+                SwingUtilities.invokeLater( new Runnable() {
+                    @Override
+                    public void run() {
+                        expandToRoot();
+                    }
+                });
+            }
         }
         else
         {
-//            if( controller == null || controller.getStepOption() != 1 )
-//                tree.collapsePath( new TreePath( this.getPath() ) );
+            if( controller == null || controller.getStepOption() != 1 )
+            {
+                SwingUtilities.invokeLater( new Runnable() {
+                    @Override
+                    public void run() {
+                        tree.collapsePath( new TreePath( getPath() ) );
+                    }
+                });
+            }
         }
         if( breakPoint && selected )
             return CodeAction.STOP; // Breakpoint
@@ -133,4 +187,31 @@ public class PseudoCodeNode extends DefaultMutableTreeNode {
     {
         return breakPoint;
     }
+
+    public ControlFlow forwardStep( Memory m )
+    {
+        ControlFlow cf = code.runForward( m );
+        cf.setJumpBack( this );
+        return cf;
+    }
+
+    public ControlFlow emptyForwardStep( Memory m )
+    {
+        code.actions.push( (Memory mem) -> {} ); // add empty reverse function
+        ControlFlow cf = new ControlFlow( ControlFlow.STEP_OVER );
+        cf.setJumpBack( this );
+        return cf;
+    }
+
+    public void backwardStep( Memory m )
+    {
+        code.runBackward( m );
+    }
+
+    public String getDebugOutput( Memory m )
+    {
+        if( parent == null )
+            return "";
+        return ((PseudoCodeNode)parent).getDebugOutput( m );
+    }
 }

+ 229 - 0
src/animation/PseudoCodeProcessor.java

@@ -0,0 +1,229 @@
+package animation;
+
+import java.util.Stack;
+
+import animation.Memory.MemoryType;
+import animation.StackFrame.FrameType;
+
+public class PseudoCodeProcessor {
+
+    public static enum CodeStatus
+    {
+        UNFINISHED,
+        BREAKPOINT,
+        FINISHED
+    }
+    
+    private String currentDebugOutput;
+    private Memory mem;
+    private PseudoCodeNode programPointer;
+    private Stack<ControlFlow> controlStack;
+    private boolean skip = false;
+    
+    public PseudoCodeProcessor( PseudoCodeNode tree )
+    {
+        mem = new Memory();
+        mem.addFrame( new StackFrame( FrameType.FUNCTION ) );
+        programPointer = tree;
+        currentDebugOutput = "";
+        controlStack = new Stack<>();
+    }
+    
+    private CodeStatus selectNextNode( PseudoCodeNode next, PseudoCodeNode last )
+    {
+        programPointer = next;
+        last.setSelected( false );
+        switch( next.setSelected( true ) )
+        {
+        case CONTINUE:
+            skip = false;
+            return CodeStatus.UNFINISHED;
+        case SKIP:
+            return forwardStepOverUntilNotSkip();
+        case STOP:
+            return CodeStatus.BREAKPOINT;
+        }
+        return CodeStatus.UNFINISHED;
+    }
+    
+    public CodeStatus forwardStep()
+    {
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        StackFrame before = mem.removeFrame();
+        mem.addFrame( before );
+        ControlFlow cf = null;
+        if( mem.isDefined( "_call" + programPointer.getId(), MemoryType.LOCAL ) )
+        {
+            String name = "_call" + programPointer.getId();
+            mem.undeclare( name, MemoryType.LOCAL );
+            cf = programPointer.emptyForwardStep( mem );
+            cf.setBackwardAction( (Memory m) -> {
+                mem.declare( name, true, MemoryType.LOCAL );
+            });
+        }
+        else
+            cf = programPointer.forwardStep( mem );
+        controlStack.push( cf );
+        currentDebugOutput = programPointer.getDebugOutput( mem );
+        switch( cf.getStatus() )
+        {
+        case ControlFlow.STEP_INTO:
+            if( mem.isDefined( "_returnTo" + programPointer.getId(), MemoryType.GLOBAL ) )
+            {
+                String name = "_returnTo" + programPointer.getId();
+                mem.declare( name, mem.read( name, MemoryType.GLOBAL ), MemoryType.LOCAL );
+                mem.undeclare( name, MemoryType.GLOBAL );
+                cf.setBackwardAction( (Memory m) -> {
+                    mem.declare( name, mem.read( name, MemoryType.LOCAL ), MemoryType.GLOBAL );
+                    mem.undeclare( name, MemoryType.LOCAL );
+                });
+            }
+            if( programPointer.getChildCount() == 0 )
+                throw new IllegalStateException( "A Codeline without sublines tried to make a STEP_INTO." );
+            else
+                return selectNextNode( (PseudoCodeNode)programPointer.getFirstChild(), programPointer );
+        case ControlFlow.STEP_OVER:
+            if( programPointer.getParent() == null )
+                return CodeStatus.FINISHED;
+            if( before.isDefined( "_returnTo" + programPointer.getId() ) )
+            {
+                String name = "_returnTo" + programPointer.getId();
+                PseudoCodeNode nextPC = before.<PseudoCodeNode>get( name );
+                before.undeclare( name );
+                cf.setBackwardAction( (Memory m) -> {
+                    before.declare( name, nextPC );
+                });
+                return selectNextNode( nextPC, programPointer );
+            }
+            PseudoCodeNode nextPC = (PseudoCodeNode) ((PseudoCodeNode)programPointer.getParent()).getChildAfter( programPointer );
+            if( nextPC == null )
+                return selectNextNode( (PseudoCodeNode)programPointer.getParent(), programPointer );
+            else
+                return selectNextNode( nextPC, programPointer );
+        case ControlFlow.CALL:
+            PseudoCodeNode f = cf.getFunction();
+            String name = "_call" + programPointer.getId();
+            mem.declare( name, true, MemoryType.LOCAL );
+            mem.declare( "_returnTo" + f.getId(), cf.getJumpBack(), MemoryType.GLOBAL );
+            cf.setBackwardAction( (Memory m) -> {
+                m.undeclare( "_returnTo" + f.getId(), MemoryType.GLOBAL );
+                m.undeclare( name, MemoryType.LOCAL );
+            });
+            return selectNextNode( f, programPointer );
+        }
+        throw new IllegalStateException( "Unknown ControlFlow action" );
+    }
+    
+    private CodeStatus forwardStepOverUntilNotSkip() {
+        skip = true;
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = forwardStep();
+        } while( mem.getSize() > stackSize && status == CodeStatus.UNFINISHED && skip );
+        return status;
+    }
+    
+    public CodeStatus forwardStepOver()
+    {
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = forwardStep();
+        } while( mem.getSize() > stackSize && status == CodeStatus.UNFINISHED );
+        return status;
+    }
+    
+    public CodeStatus forwardStepOut()
+    {
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = forwardStep();
+        } while( mem.getSize() >= stackSize && status == CodeStatus.UNFINISHED );
+        return status;
+    }
+    
+    private CodeStatus selectBeforeNode( PseudoCodeNode next, PseudoCodeNode last )
+    {
+        programPointer = next;
+        last.setSelected( false );
+        switch( next.setSelected( true ) )
+        {
+        case CONTINUE:
+            skip = false;
+            return CodeStatus.UNFINISHED;
+        case SKIP:
+            return backwardStepOverUntilNotSkip();
+        case STOP:
+            return CodeStatus.BREAKPOINT;
+        }
+        return CodeStatus.UNFINISHED;
+    }
+    
+    public CodeStatus backwardStep()
+    {
+        if( programPointer == null || controlStack.isEmpty() )
+            return CodeStatus.FINISHED;
+        ControlFlow cf = controlStack.pop();
+        PseudoCodeNode nextPC = cf.getJumpBack();
+        cf.backward( mem );
+        nextPC.backwardStep( mem );
+        currentDebugOutput = nextPC.getDebugOutput( mem );
+        return selectBeforeNode( nextPC, programPointer );
+    }
+    
+    public CodeStatus backwardStepOverUntilNotSkip()
+    {
+        skip = true;
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = backwardStep();
+        } while( mem.getSize() > stackSize && status == CodeStatus.UNFINISHED && skip );
+        return status;
+    }
+    
+    public CodeStatus backwardStepOver()
+    {
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = backwardStep();
+        } while( mem.getSize() > stackSize && status == CodeStatus.UNFINISHED );
+        return status;
+    }
+    
+    public CodeStatus backwardStepOut()
+    {
+        if( programPointer == null )
+            return CodeStatus.FINISHED;
+        int stackSize = mem.getSize();
+        CodeStatus status = CodeStatus.UNFINISHED;
+        do {
+            status = backwardStep();
+        } while( mem.getSize() >= stackSize && status == CodeStatus.UNFINISHED );
+        return status;
+    }
+    
+    public Memory getMemory()
+    {
+        return mem;
+    }
+    
+    public String getDebugOutput()
+    {
+        return currentDebugOutput;
+    }
+}

+ 51 - 0
src/animation/StackFrame.java

@@ -0,0 +1,51 @@
+package animation;
+
+import java.util.HashMap;
+
+public class StackFrame {
+
+	public enum FrameType {
+		FUNCTION,
+		LOOP
+	}
+	
+    private HashMap< String, Object > data;
+    private FrameType type;
+    
+    public StackFrame( FrameType type )
+    {
+    	this.type = type;
+        data = new HashMap< String, Object >();
+    }
+    
+    public FrameType getType()
+    {
+    	return type;
+    }
+    
+    public <T> void declare( String name, T value )
+    {
+        data.put( name, (Object)value );
+    }
+    
+    public <T> void set( String name, T value )
+    {
+        data.put( name, (Object)value );
+    }
+    
+    public void undeclare( String name )
+    {
+        data.remove( name );
+    }
+    
+    public boolean isDefined( String name )
+    {
+        return data.containsKey( name );
+    }
+    
+    @SuppressWarnings("unchecked")
+    public <T> T get( String name )
+    {
+        return (T)data.get( name );
+    }
+}

+ 151 - 426
src/bk/BKNodePlacement.java

@@ -1,14 +1,22 @@
 package bk;
 
-import java.lang.reflect.InvocationTargetException;
-
 import javax.swing.JFrame;
 import javax.swing.JTree;
 
 import animation.AnimatedAlgorithm;
 import animation.AnimationController;
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
+import animation.PseudoCodeProcessor;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import codelines.DeclareVariable;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.Comment;
+import codelines.SetVariable;
 import graph.LayeredGraphNode;
 import lib.TextLayoutHelper;
 
@@ -19,10 +27,6 @@ import lib.TextLayoutHelper;
  */
 public class BKNodePlacement extends AnimatedAlgorithm {
 
-    /*
-     * Private data structures to store the process of the algorithm
-     */
-
     public enum State
     {
         CONFLICTS,
@@ -33,29 +37,11 @@ public class BKNodePlacement extends AnimatedAlgorithm {
         COMBINE
     }
 
-    private ConflictDetection conftion;
     private State state;
-    private ExtremalLayoutCalc layouts[];
-    private Combine combine;
-    private PseudoCodeNode conflictsNode; 
-    private PseudoCodeNode layout1Node;
-    private PseudoCodeNode layout2Node;
-    private PseudoCodeNode layout3Node;
-    private PseudoCodeNode layout4Node;
-    private PseudoCodeNode combineNode;
-    private boolean inside;
 
     public BKNodePlacement(AnimationController controller, LayeredGraphNode graph, JFrame view) {
         super(controller, graph, view);
         state = State.CONFLICTS;
-        conftion = new ConflictDetection( graph );
-        layouts = new ExtremalLayoutCalc[ 4 ];
-        layouts[ 0 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.TOP_BOTTOM_LEFT, graph );
-        layouts[ 1 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.TOP_BOTTOM_RIGHT, graph );
-        layouts[ 2 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.BOTTOM_TOP_LEFT, graph );
-        layouts[ 3 ] = new ExtremalLayoutCalc( ExtremalLayoutCalc.LayoutType.BOTTOM_TOP_RIGHT, graph );
-        combine = new Combine( graph );
-        inside = false;
     }
     
     public State getAlgorithmState()
@@ -63,413 +49,152 @@ public class BKNodePlacement extends AnimatedAlgorithm {
         return state;
     }
 
-    @Override
-    public StageStatus forwardStep() {
-        graph.unselectGraph();
-        return forward( "forwardStep" );
-    }
-
-    @Override
-    public StageStatus backwardStep() {
-        graph.unselectGraph();
-        return backward( "backwardStep" );
-    }
-
+    @SuppressWarnings("serial")
     @Override
     public PseudoCodeNode createPseudocodeTree( JTree tree )
     {
-        PseudoCodeNode root = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("BK Node Placement Algorithm"), tree );
+    	String[] vars = { "layout", "graph" };
+    	PseudoCodeNode mainFunction = new PseudoCodeNode( "function bkNodePlacement( graph )", vars, tree, new FunctionDefinition( new String[]{"graph"} ) );
+    	root = new PseudoCodeNode( "-- BK Node Placement Algorithm --", vars, tree, new CodeLine() {
+
+			@Override
+			public ControlFlow runForward(Memory m) {
+				m.declare( "param1", graph, MemoryType.GLOBAL );
+				if( m.isDefined( "Called", MemoryType.GLOBAL ) )
+				{
+					actions.push( (Memory mem) -> {
+					    m.undeclare( "param1", MemoryType.GLOBAL );
+					} );
+					return new ControlFlow( ControlFlow.STEP_OVER );
+				}
+				m.declare( "Called", true, MemoryType.GLOBAL );
+				actions.push( (Memory mem) -> {
+                    m.undeclare( "param1", MemoryType.GLOBAL );
+	                m.undeclare( "Called", MemoryType.GLOBAL );
+				} );
+				return new ControlFlow( mainFunction );
+			}
+
+        } );
         root.setSelected( true );
-        conflictsNode = conftion.createPseudocodeTree( tree );
-        layout1Node = layouts[ 0 ].createPseudocodeTree( tree );
-        layout2Node = layouts[ 1 ].createPseudocodeTree( tree );
-        layout3Node = layouts[ 2 ].createPseudocodeTree( tree );
-        layout4Node = layouts[ 3 ].createPseudocodeTree( tree );
-        combineNode = combine.createPseudocodeTree( tree );
-        root.add( conflictsNode );
-        root.add( layout1Node );
-        root.add( layout2Node );
-        root.add( layout3Node );
-        root.add( layout4Node );
-        root.add( combineNode );
-        return root;
-    }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !inside )
-        {
-            State oldState = state;
-            StageStatus status = StageStatus.UNFINISHED;
-            while( state == oldState && status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-            return forward( "forwardStepOver" );
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-            return forward( "forwardStepOut" );
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !inside )
-        {
-            State oldState = state;
-            StageStatus status = StageStatus.UNFINISHED;
-            while( state == oldState && status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-            return backward( "backwardStepOver" );
-    }
 
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-            return backward( "backwardStepOut" );
-    }
-
-    @Override
-    public String getDebugString()
-    {
-        if( !inside )
-            return "";
-        switch( state )
-        {
-        case CONFLICTS:
-            return conftion.getDebugString();
-        case LAYOUT1:
-            return layouts[ 0 ].getDebugString();
-        case LAYOUT2:
-            return layouts[ 1 ].getDebugString();
-        case LAYOUT3:
-            return layouts[ 2 ].getDebugString();
-        case LAYOUT4:
-            return layouts[ 3 ].getDebugString();
-        case COMBINE:
-            return combine.getDebugString();
-        }
-        return "";
-    }
-    
-    private StageStatus forward( String fName )
-    {
-        boolean breakpoint = false;
-        CodeAction action = null;
-        try {
-            switch( state )
-            {
-            case CONFLICTS:
-                boolean selected = conflictsNode.isSelected();
-                action = conflictsNode.setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( (StageStatus)(ConflictDetection.class.getMethod( fName ).invoke( conftion ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        conflictsNode.setSelected( false );
-                        CodeAction ca = layout1Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT1;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT1:
-                action = layout1Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 0 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout1Node.setSelected( false );
-                        CodeAction ca = layout2Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT2;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT2:
-                action = layout2Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 1 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout2Node.setSelected( false );
-                        CodeAction ca = layout3Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT3;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT3:
-                action = layout3Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 2 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout3Node.setSelected( false );
-                        CodeAction ca = layout4Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT4;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT4:
-                action = layout4Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 3 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout4Node.setSelected( false );
-                        CodeAction ca = combineNode.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.COMBINE;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case COMBINE:
-                action = combineNode.setSelected( true );
-                do {
-                    switch( (StageStatus)(Combine.class.getMethod( fName ).invoke( combine ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        return StageStatus.FINISHED;
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
+        PseudoCodeNode conflictDetectionFunction = ConflictDetection.mark_conflicts( tree );
+        PseudoCodeNode calcLayout = new PseudoCodeNode( "function calcLayout( layout, graph )", vars, tree, new FunctionDefinition( vars ) );
+        PseudoCodeNode combine = Combine.combine( tree );
+        root.add( mainFunction );
+        mainFunction.add( new PseudoCodeNode( "call detectConflicts( graph )", vars, tree, new FunctionCall( conflictDetectionFunction, new String[]{ "graph" } ) ) );
+        mainFunction.add( new PseudoCodeNode( "layout = 'DOWN_RIGHT'", vars, tree, new DeclareVariable<String>( "layout" ) {
+            @Override
+            protected String value(ReadOnlyMemory m) {
+                return "DOWN_RIGHT";
             }
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (IllegalArgumentException e) {
-            e.printStackTrace();
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        } catch (SecurityException e) {
-            e.printStackTrace();
-        }
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        return StageStatus.UNFINISHED;
-    }
-
-    private StageStatus backward( String fName ) {
-        boolean breakpoint = false;
-        CodeAction action = null;
-        try {
-            switch( state )
-            {
-            case CONFLICTS:
-                action = conflictsNode.setSelected( true );
-                do {
-                    switch( (StageStatus)(ConflictDetection.class.getMethod( fName ).invoke( conftion ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        conflictsNode.setSelected( false );
-                        return StageStatus.FINISHED;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-            case LAYOUT1:
-                action = layout1Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 0 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout1Node.setSelected( false );
-                        CodeAction ca = conflictsNode.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.CONFLICTS;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return backward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT2:
-                action = layout2Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 1 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout2Node.setSelected( false );
-                        CodeAction ca = layout1Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT1;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return backward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT3:
-                action = layout3Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 2 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout3Node.setSelected( false );
-                        CodeAction ca = layout2Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT2;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return backward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case LAYOUT4:
-                action = layout4Node.setSelected( true );
-                do {
-                    switch( (StageStatus)(ExtremalLayoutCalc.class.getMethod( fName ).invoke( layouts[ 3 ] ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        layout4Node.setSelected( false );
-                        CodeAction ca = layout3Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT3;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return backward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
-            case COMBINE:
-                action = combineNode.setSelected( true );
-                do {
-                    switch( (StageStatus)(Combine.class.getMethod( fName ).invoke( combine ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        combineNode.setSelected( false );
-                        CodeAction ca = layout4Node.setSelected( true );
-                        breakpoint |= ca == CodeAction.STOP;
-                        state = State.LAYOUT4;
-                        if( ca == CodeAction.SKIP && !breakpoint )
-                            return backward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                break;
+        }) );
+        mainFunction.add( new PseudoCodeNode( "call calcLayout( layout, graph )", vars, tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
+        mainFunction.add( new PseudoCodeNode( "layout = 'DOWN_LEFT'", vars, tree, new SetVariable<String>( "layout" ) {
+            @Override
+            protected String value(ReadOnlyMemory m) {
+                return "DOWN_LEFT";
+            }
+        }));
+        mainFunction.add( new PseudoCodeNode( "call calcLayout( layout, graph )", vars, tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
+        mainFunction.add( new PseudoCodeNode( "layout = 'UP_RIGHT'", vars, tree, new SetVariable<String>( "layout" ) {
+            @Override
+            protected String value(ReadOnlyMemory m) {
+                return "UP_RIGHT";
+            }
+        }) );
+        mainFunction.add( new PseudoCodeNode( "call calcLayout( layout, graph )", vars, tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
+        mainFunction.add( new PseudoCodeNode( "layout = 'UP_LEFT'", vars, tree, new SetVariable<String>( "layout" ) {
+            @Override
+            protected String value(ReadOnlyMemory m) {
+                return "UP_LEFT";
+            }
+        }) );
+        mainFunction.add( new PseudoCodeNode( "call calcLayout( layout, graph )", vars, tree, new FunctionCall( calcLayout, new String[]{ "layout", "graph" } ) ) );
+        mainFunction.add( new PseudoCodeNode( "call combine( graph )", vars, tree, new FunctionCall( combine, new String[]{ "graph" } ) ) );
+        PseudoCodeNode conflictsStage = new PseudoCodeNode( "-- mark type 1 conflicts --", vars, tree, new Comment() ) {
+            @Override
+            public String getDebugOutput( Memory m ) {
+                state = State.CONFLICTS;
+                return ConflictDetection.buildDebugString( m );
             }
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (IllegalArgumentException e) {
-            e.printStackTrace();
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        } catch (SecurityException e) {
-            e.printStackTrace();
-        }
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        return StageStatus.UNFINISHED;
+        };
+        root.add( conflictsStage );
+        conflictsStage.add( conflictDetectionFunction );
+        PseudoCodeNode blockCalc = BlockCalc.calculateBlockGraph( tree, calcLayout );
+        PseudoCodeNode placeBlock = PlaceBlock.place_block( tree );
+        PseudoCodeNode horizontalCompaction = Compaction.horizontalCompaction( tree, placeBlock );
+        calcLayout.add( new PseudoCodeNode( "call calculateBlockGraph( layout, graph )", vars, tree, new FunctionCall( blockCalc, vars ) ) );
+        calcLayout.add( new PseudoCodeNode( "call horizontalCompaction( layout, graph )", vars, tree, new FunctionCall( horizontalCompaction, vars ) ) );
+        PseudoCodeNode extremalLayoutStage = new PseudoCodeNode( "-- Compute an extremal layout --", vars, tree, new Comment() ) {
+            @Override
+            public String getDebugOutput( Memory m ) {
+                if( !m.isSomewhereDefined( "graph", MemoryType.COMPLETE_STACK ) || !m.isSomewhereDefined( "layout", MemoryType.COMPLETE_STACK ) )
+                    return "";
+                String info = "| Node | Shift | Sink | Root | Align |  x  |  xDef  |\n";
+                info +=       "|------|-------|------|------|-------|-----|--------|\n";
+                LayeredGraphNode graph = m.read( "graph", MemoryType.COMPLETE_STACK );
+                LayoutType type = LayoutType.fromString( m.read( "layout", MemoryType.COMPLETE_STACK ) );
+                switch( type ) {
+                case TOP_BOTTOM_LEFT:
+                    state = State.LAYOUT1;
+                    break;
+                case TOP_BOTTOM_RIGHT:
+                    state = State.LAYOUT2;
+                    break;
+                case BOTTOM_TOP_LEFT:
+                    state = State.LAYOUT3;
+                    break;
+                case BOTTOM_TOP_RIGHT:
+                    state = State.LAYOUT4;
+                    break;
+                case COMBINED:
+                    state = State.COMBINE;
+                    break;
+                }
+                for( LayeredGraphNode n : graph.getContainedNodes() )
+                {
+                    info += "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getShift( type ) + "", 7 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getSink( type ).getName(), 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getRoot( type ).getName(), 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getAlign( type ).getName(), 7 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( type ) + "", 5 ) +
+                            "|" + TextLayoutHelper.strToLen( !n.isXUndefined( type ) + "", 8 ) + "|\n";
+                }
+                return info;
+            }
+        };
+        root.add( extremalLayoutStage );
+        extremalLayoutStage.add( calcLayout );
+        extremalLayoutStage.add( new PseudoCodeNode( "-- vertical alignment --", vars, tree, new Comment() ) );
+        extremalLayoutStage.add( blockCalc );
+        extremalLayoutStage.add( new PseudoCodeNode( "-- horizontal compaction --", vars, tree, new Comment() ) );
+        extremalLayoutStage.add( horizontalCompaction );
+        extremalLayoutStage.add( placeBlock );
+        PseudoCodeNode balancingStage = new PseudoCodeNode( "-- balancing --", vars, tree, new Comment() ) {
+            @Override
+            public String getDebugOutput( Memory m ) {
+                if( !m.isSomewhereDefined( "graph", MemoryType.COMPLETE_STACK ) )
+                    return "";
+                String info = "| Node | x BR | x BL | x UR | x UL | x CO |\n";
+                info +=       "|------|------|------|------|------|------|\n";
+                LayeredGraphNode graph = m.read( "graph", MemoryType.COMPLETE_STACK );
+                for( LayeredGraphNode n : graph.getContainedNodes() )
+                {
+                    info += "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.TOP_BOTTOM_LEFT ) + "", 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.TOP_BOTTOM_RIGHT ) + "", 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.BOTTOM_TOP_LEFT ) + "", 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.BOTTOM_TOP_LEFT ) + "", 6 ) +
+                            "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.COMBINED ) + "", 6 ) + "|\n";
+                }
+                return info;
+            }
+        };
+        root.add( balancingStage );
+        balancingStage.add( combine );
+        processor = new PseudoCodeProcessor( root );
+        return root;
     }
-    
 }

+ 224 - 440
src/bk/BlockCalc.java

@@ -1,468 +1,252 @@
 package bk;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.List;
 
 import javax.swing.JTree;
 
-import animation.AlgorithmStage;
-import animation.BackwardAction;
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
-import bk.ExtremalLayoutCalc.LayoutType;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import codelines.AbstractForLoop;
+import codelines.DeclareVariable;
+import codelines.ForEachLoop;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.IfLoop;
+import codelines.SetVariable;
 import graph.LayeredGraphEdge;
 import graph.LayeredGraphNode;
-import lib.TextLayoutHelper;
 
 /**
  * The stage of the BK node placement algorithm where the blocks are computed.
  * @author kolja
  *
  */
-public class BlockCalc implements AlgorithmStage {
-
-    private int layerIndex;
-    private int nodeIndex;
-    private int r;
-    private LayeredGraphNode graph;
-    private ArrayList< ArrayList< PseudoCodeNode > > subgraphNodes;
-    private ArrayList< ArrayList< ExtremalLayoutCalc > > subgraphAlgs;
-    private ArrayList< BackwardAction > backwards; // TODO: evtl richtigen "Stack" benutzen
-    private LayoutType layout;
-    private PseudoCodeNode loopNode;
-    private boolean inside;
-    int step;
-
-    public BlockCalc( LayeredGraphNode graph, LayoutType layout )
-    {
-        this.layout = layout;
-        step = 0;
-        this.graph = graph;
-        layerIndex = 0;
-        nodeIndex = 0;
-        r = 0;
-        subgraphNodes = new ArrayList<>();
-        subgraphAlgs = new ArrayList<>();
-        for( ArrayList<LayeredGraphNode> l : graph.getContainedLayers() )
-        {
-            ArrayList< PseudoCodeNode > nodes = new ArrayList<>();
-            ArrayList< ExtremalLayoutCalc > algs = new ArrayList<>();
-            for( int i = 0; i < l.size(); i++ )
+public class BlockCalc {
+
+    public static PseudoCodeNode calculateBlockGraph( JTree tree, PseudoCodeNode claclLayout ) {
+        String[] vars = { "graph", "L", "r", "neighbors", "layout", "m", "i", "k", "mids", "n" };
+        @SuppressWarnings("serial")
+        PseudoCodeNode root = new PseudoCodeNode( "function calculateBlockGraph( layout, graph )", vars, tree, new FunctionDefinition( new String[]{ "layout", "graph" } ) ) {
+            @Override
+            public String getDebugOutput( Memory m )
             {
-                nodes.add( null );
-                algs.add( null );
+                if( m.isSomewhereDefined( "n", MemoryType.LOCAL ) )
+                    m.<LayeredGraphNode>read( "n", MemoryType.LOCAL).setSelected( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                return super.getDebugOutput( m );
             }
-            subgraphAlgs.add( algs );
-            subgraphNodes.add( nodes );
-        }
-        inside = false;
-        backwards = new ArrayList<>();
-    }
-
-    private int calcLayerIndex()
-    {
-        if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-            return layerIndex;
-        if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            return graph.getContainedLayers().size() - layerIndex - 1;
-        return -1;
-    }
-
-    private int calcBeforeLayerIndex()
-    {
-        if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-            return layerIndex - 1;
-        if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            return graph.getContainedLayers().size() - layerIndex;
-        return -1;
-    }
-
-    private int calcNodeIndex( int index )
-    {
-        if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.BOTTOM_TOP_LEFT )
-            return index;
-        if( layout == LayoutType.TOP_BOTTOM_RIGHT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            return graph.getContainedLayers().get( calcLayerIndex() ).size() - index - 1;
-        return index;
-    }
-    
-    private int calcBeforeLayerNodeIndex( int index )
-    {
-        if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.BOTTOM_TOP_LEFT )
-            return index;
-        if( layout == LayoutType.TOP_BOTTOM_RIGHT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            return graph.getContainedLayers().get( calcBeforeLayerIndex() ).size() - index - 1;
-        return index;
-    }
-
-    @Override
-    public StageStatus forwardStep() {
-        LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) );
-        current.setSelected( layout );
-        if( current.getContainedNodes().size() > 0 )
-        {
-            inside = true;
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).forwardStep() )
+        };
+        root.add( new PseudoCodeNode( "L = graph.getContainedLayers();", vars, tree, new DeclareVariable<ArrayList<ArrayList<LayeredGraphNode>>>( "L" ) {
+            @Override
+            protected ArrayList<ArrayList<LayeredGraphNode>> value(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers();
+            }
+        } ) );
+        PseudoCodeNode layerLoop = new PseudoCodeNode( "for i=layout.contains('DOWN') ? 0 : |L|-1 to layout.contains('DOWN') ? |L|-1 : 0 do", vars, tree, new AbstractForLoop<Integer>( "i" ) {
+            @Override
+            protected Integer begin(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return 0;
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
+            }
+            @Override
+            protected Integer step(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) + 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) - 1;
+            }
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) <= m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) >= 0;
+            }
+        });
+        root.add( layerLoop );
+        layerLoop.add( new PseudoCodeNode( "r = layout.contains('RIGHT') ? -1 : INFINITY;", vars, tree, new DeclareVariable<Double>( "r" ) {
+            @Override
+            protected Double value(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return -1.0;
+                return Double.POSITIVE_INFINITY;
+            }
+        } ) );
+        PseudoCodeNode nodeLoop = new PseudoCodeNode( "for k=layout.contains('RIGHT') ? 0 : |L[i]|-1 to layout.contains('RIGHT') ? |L[i]|-1 : 0 do", vars, tree, new AbstractForLoop<Integer>( "k" ) {
+            @Override
+            protected Integer begin( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return 0;
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).size() - 1;
+            }
+            @Override
+            protected Integer step( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return m.<Integer>read( "k", MemoryType.LOCAL ) + 1;
+                return m.<Integer>read( "k", MemoryType.LOCAL ) - 1;
+            }
+            @Override
+            protected boolean condition( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return m.<Integer>read( "k", MemoryType.LOCAL ) <= m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).size() - 1;
+                return m.<Integer>read( "k", MemoryType.LOCAL ) >= 0;
+            }
+        });
+        nodeLoop.add( new PseudoCodeNode( "n = L[i][k];", vars, tree, new DeclareVariable<LayeredGraphNode>( "n" ) {
+            @Override
+            protected LayeredGraphNode value(ReadOnlyMemory m) {
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).get( m.read( "k", MemoryType.LOCAL ) );
+            }
+        }));
+        PseudoCodeNode ifNode = new PseudoCodeNode( "if n has subgraph then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getContainedNodes().size() > 0;
+            }
+        } );
+        nodeLoop.add( ifNode );
+        ifNode.add( new PseudoCodeNode( "call calcLayout( layout, n );", vars, tree, new FunctionCall( claclLayout, new String[]{ "layout", "n" } ) ) );
+        nodeLoop.add( new PseudoCodeNode( "neighbors = layout.contains('DOWN') ? predecessors(n) : successors(n)", vars, tree, new DeclareVariable<ArrayList<LayeredGraphNode>>( "neighbors" ) {
+            @Override
+            protected ArrayList<LayeredGraphNode> value(ReadOnlyMemory m) {
+                ArrayList<LayeredGraphEdge> list = m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getSortedOutgoingEdges();
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    list = m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getSortedIncomingEdges();
+                ArrayList<LayeredGraphNode> result = new ArrayList<LayeredGraphNode>();
+                for( LayeredGraphEdge e : list )
                 {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
+                    if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                        result.add( e.getSources().get( 0 ) );
+                    else
+                        result.add( e.getTargets().get( 0 ) );
                 }
-            } while( !breakpoint && action == CodeAction.SKIP );
-        }
-        ArrayList< LayeredGraphEdge > incommingEdges = null;
-        if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-            incommingEdges = current.getSortedIncomingEdges();
-        if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            incommingEdges = current.getSortedOutgoingEdges();
-        if( layout == LayoutType.TOP_BOTTOM_RIGHT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-        Collections.reverse( incommingEdges );
-        if( incommingEdges.size() == 0 )
-        {
-            backwards.add( 0, () -> {
-                System.out.println( "performing empty backwards step..." );
-            });
-            return calcNextState();
-        }
-        int[] ms = {(incommingEdges.size() + 1) / 2, (int)( (incommingEdges.size() + 1) / 2.0 + 0.5 )};
-        boolean backwardsAdded = false;
-        for( int m : ms )
-        {
-            if( current.getAlign( layout ) == current )
-            {
-                LayeredGraphNode u = null;
-                if( layout == LayoutType.TOP_BOTTOM_LEFT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-                    u = incommingEdges.get( m - 1 ).getSources().get( 0 );
-                if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-                    u = incommingEdges.get( m - 1 ).getTargets().get( 0 );
-                ArrayList<LayeredGraphEdge> conflicts = incommingEdges.get( m - 1 ).calcEdgeCrossings();
-                
-                if( !incommingEdges.get( m - 1 ).isConflicted( layout ) && r < calcBeforeLayerNodeIndex( graph.getContainedLayers().get( calcBeforeLayerIndex() ).indexOf( u ) ) + 1 )
+                return result;
+            }
+        } ) );
+        layerLoop.add( nodeLoop );
+        nodeLoop.add( new PseudoCodeNode( "neighbors = layout.contains('RIGHT') ? neighbors : reverse( neighbors )", vars, tree, new SetVariable<ArrayList<LayeredGraphNode>>( "neighbors" ) {
+            @Override
+            protected ArrayList<LayeredGraphNode> value(ReadOnlyMemory m) {
+                ArrayList<LayeredGraphNode> list = m.read( "neighbors", MemoryType.LOCAL );
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return list;
+                else
                 {
-                    ArrayList< Boolean > oldConflicts = new ArrayList<>();
-                    for( LayeredGraphEdge e : conflicts )
-                    {
-                        oldConflicts.add( e.isConflicted( layout ) );
-                        e.setConflicted( true, layout );
-                    }
-                    LayeredGraphNode oldAlignU = u.getAlign( layout );
-                    LayeredGraphNode oldRootCurrent = current.getRoot( layout );
-                    LayeredGraphNode oldAlignCurrent = current.getAlign( layout );
-                    int oldR = r;
-                    u.setAlign( current, layout );
-                    current.setRoot( u.getRoot( layout ), layout );
-                    current.setAlign( current.getRoot( layout ), layout );
-                    r = calcBeforeLayerNodeIndex( graph.getContainedLayers().get( calcBeforeLayerIndex() ).indexOf( u ) ) + 1;
-                    int oldStep = step++;
-                    final LayeredGraphNode uf = u;
-                    backwards.add( 0, () -> {
-                        System.out.println( "Stepping Backwards... (Step " + oldStep + ")" );
-                        for( int i = 0; i < conflicts.size(); i++ )
-                            conflicts.get( i ).setConflicted( oldConflicts.get( i ), layout );
-                        uf.setAlign( oldAlignU, layout );
-                        current.setRoot( oldRootCurrent, layout );
-                        current.setAlign( oldAlignCurrent, layout );
-                        r = oldR;
-                    });
-                    backwardsAdded = true;
+                    ArrayList<LayeredGraphNode> result = new ArrayList<LayeredGraphNode>();
+                    for( int i = list.size() - 1; i >= 0; i-- )
+                        result.add( list.get( i ) );
+                    return result;
                 }
             }
-        }
-        if( !backwardsAdded )
-        {
-            backwards.add( 0, () -> {
-                System.out.println( "performing empty backwards step..." );
-            });
-        }
-        CodeAction action = loopNode.setSelected( true );
-        boolean breakpoint = action == CodeAction.STOP;
-        StageStatus status = calcNextState();
-        if( status == StageStatus.FINISHED )
-            return status;
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        if( action == CodeAction.SKIP )
-            return forwardStep();
-        return status;
-    }
-
-    private StageStatus calcNextState()
-    {
-        if( layerIndex >= graph.getContainedLayers().size() - 1 )
-        {
-            if( nodeIndex >= graph.getContainedLayers().get( calcLayerIndex() ).size() -1 )
-            {
-                loopNode.setSelected( false );
-                return StageStatus.FINISHED;
+        } ) );
+        PseudoCodeNode ifNeighbors = new PseudoCodeNode( "if |neighbors| > 0 then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition( ReadOnlyMemory m) {
+                return m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).size() > 0;
             }
-        }
-        nodeIndex++;
-        if( nodeIndex >= graph.getContainedLayers().get( calcLayerIndex() ).size() )
-        {
-            layerIndex++;
-            nodeIndex = 0;
-            int oldR = r;
-            r = 0;
-            backwards.add(0, ()->{
-               this.r = oldR;
-            });
-        }
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public StageStatus backwardStep() {
-        if( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ) != null )
-        {
-            inside = true;
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).backwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( calcNodeIndex(  nodeIndex ) );
-                    current.setSelected( layout );
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-        }
-        CodeAction action = loopNode.setSelected( true );
-        boolean breakpoint = action == CodeAction.STOP;
-        LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) );
-        current.setSelected( layout );
-        if( !backwards.isEmpty() )
-        {
-            backwards.get( 0 ).reverse();
-            backwards.remove( 0 );
-        }
-        StageStatus status = calcBeforeState();
-        if( status == StageStatus.FINISHED )
-            return status;
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        if( action == CodeAction.SKIP )
-            return backwardStep();
-        return status;
-    }
-
-    private StageStatus calcBeforeState()
-    {
-        if( layerIndex == 0 )
-        {
-            if( nodeIndex == 0 )
-            {
-                loopNode.setSelected( false );
-                return StageStatus.FINISHED;
+        });
+        nodeLoop.add( ifNeighbors );
+        ifNeighbors.add( new PseudoCodeNode( "mids = [roundDown((|neighbors|-1)/2),roundUp((|neighbors|-1)/2)]", vars, tree, new DeclareVariable<ArrayList<Integer>>( "mids" ) {
+            @Override
+            protected ArrayList<Integer> value(ReadOnlyMemory m) {
+                int size = m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).size() - 1;
+                int m1 = size / 2, m2 = (int)(size / 2.0 + 0.5);
+                ArrayList<Integer> list = new ArrayList<Integer>();
+                list.add( m1 );
+                if( m1 != m2 )
+                    list.add( m2 );
+                return list;
             }
-        }
-        nodeIndex--;
-        if( nodeIndex < 0 )
-        {
-            layerIndex--;
-            backwards.get( 0 ).reverse();
-            backwards.remove( 0 );
-            nodeIndex = graph.getContainedLayers().get( calcLayerIndex() ).size() - 1;
-        }
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
-        PseudoCodeNode root = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Vertical alignment"), tree );
-        loopNode = new PseudoCodeNode( "Loop through all nodes...", tree );
-        do {
-            LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) );
-            if( current.getContainedNodes().size() > 0 )
-            {
-                ExtremalLayoutCalc extcalc = new ExtremalLayoutCalc( layout, current );
-                PseudoCodeNode subNode = extcalc.createPseudocodeTree( loopNode.getTree() );
-                loopNode.add( subNode );
-                subgraphAlgs.get( calcLayerIndex() ).set( calcNodeIndex( nodeIndex ), extcalc );
-                subgraphNodes.get( calcLayerIndex() ).set( calcNodeIndex( nodeIndex ), subNode );
+        } ) );
+        PseudoCodeNode midLoop = new PseudoCodeNode( "foreach m in mids do", vars, tree, new ForEachLoop<Integer>( "m" ) {
+            @Override
+            protected List<Integer> list(ReadOnlyMemory m) {
+                return m.read( "mids", MemoryType.LOCAL );
+            }
+        } );
+        ifNeighbors.add( midLoop );
+        PseudoCodeNode ifAlign = new PseudoCodeNode( "if align[n] == n then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayeredGraphNode n = m.read( "n", MemoryType.LOCAL );
+                return n.getAlign( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) ) == n;
+            }
+        });
+        midLoop.add( ifAlign );
+        PseudoCodeNode ifMarked = new PseudoCodeNode( "if (neighbors[m],n) not conflicted and ((r < pos(neighbors[m]) and layout.contains('RIGHT')) or (r > pos(neighbors[m]) and layout.contains('LEFT'))) then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayeredGraphEdge e = m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).findEdgeBetween(
+                        m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) ),
+                        m.read( "n", MemoryType.LOCAL ) );
+                if( e == null )
+                    e = m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).findEdgeBetween( m.read( "n", MemoryType.LOCAL ),
+                            m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) ) );
+                ArrayList<LayeredGraphNode> layerBefore;
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    layerBefore = m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.<Integer>read( "i", MemoryType.LOCAL ) - 1 );
+                else
+                    layerBefore = m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 );
+                int posU = layerBefore.indexOf( m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) ) );
+                return !e.isConflicted( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) ) &&
+                        ( ( m.<Double>read( "r", MemoryType.LOCAL ) < posU && m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) ) ||
+                                ( m.<Double>read( "r", MemoryType.LOCAL ) > posU && m.<String>read( "layout", MemoryType.LOCAL ).contains( "LEFT" ) ) );
             }
-        } while( calcNextState() != StageStatus.FINISHED );
-        layerIndex = 0;
-        nodeIndex = 0;
-        backwards.clear();
-        root.add( loopNode );
+        });
+        ifAlign.add( ifMarked );
+        ifMarked.add( new PseudoCodeNode( "align[neighbors[m]] = n;", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode u = m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) );
+                LayeredGraphNode v = m.read( "n", MemoryType.LOCAL );
+                LayeredGraphNode old = u.getAlign( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                u.setAlign( v, LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                actions.push( (Memory mem) -> {
+                    u.setAlign( old, LayoutType.fromString( mem.read( "layout", MemoryType.LOCAL ) ) );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }) );
+        ifMarked.add( new PseudoCodeNode( "root[n] = root[neighbors[m]];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode u = m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) );
+                LayeredGraphNode v = m.read( "n", MemoryType.LOCAL );
+                LayeredGraphNode old = v.getRoot( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                v.setRoot( u.getRoot( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) ), LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                actions.push( (Memory mem) -> {
+                    v.setRoot( old, LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }) );
+        ifMarked.add( new PseudoCodeNode( "align[n] = root[n];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode v = m.read( "n", MemoryType.LOCAL );
+                LayeredGraphNode old = v.getAlign( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                v.setAlign( v.getRoot( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) ), LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                actions.push( (Memory mem) -> {
+                    v.setAlign( old, LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }) );
+        ifMarked.add( new PseudoCodeNode( "r = pos(neighbors[m]);", vars, tree, new SetVariable<Double>( "r" ) {
+            @Override
+            protected Double value(ReadOnlyMemory m) {
+                ArrayList<LayeredGraphNode> layerBefore;
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    layerBefore = m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.<Integer>read( "i", MemoryType.LOCAL ) - 1 );
+                else
+                    layerBefore = m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 );
+                LayeredGraphNode u = m.<ArrayList<LayeredGraphNode>>read( "neighbors", MemoryType.LOCAL ).get( m.read( "m", MemoryType.LOCAL ) );
+                return (double)layerBefore.indexOf( u );
+            }
+        }) );
         return root;
     }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !inside )
-            return forwardStep();
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).forwardStepOver() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).forwardStepOut() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !inside )
-            return backwardStep();
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).backwardStepOver() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( nodeIndex );
-                    current.setSelected( layout );
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).isSelected();
-            CodeAction action = subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).backwardStepOut() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedLayers().get( calcLayerIndex() ).get( nodeIndex );
-                    current.setSelected( layout );
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    inside = false;
-                    subgraphNodes.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-    
-    @Override
-    public String getDebugString()
-    {
-        if( inside )
-        {
-            String info = "Subgraph of " + graph.getContainedLayers().get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).getName() + ":\n";
-            String tmp = subgraphAlgs.get( calcLayerIndex() ).get( calcNodeIndex( nodeIndex ) ).getDebugString();
-            info += tmp;
-            return info;
-        }
-        return "";
-    }
 }

+ 200 - 438
src/bk/Combine.java

@@ -3,204 +3,226 @@ package bk;
 import java.awt.Color;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
 import javax.swing.JTree;
 
-import animation.AlgorithmStage;
-import animation.BackwardAction;
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
-import bk.ExtremalLayoutCalc.LayoutType;
+import codelines.Comment;
+import codelines.DeclareVariable;
+import codelines.ForEachLoop;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.IfLoop;
+import codelines.SetVariable;
 import graph.LayeredGraphNode;
-import lib.TextLayoutHelper;
 
 /**
  * The stage of the combination of the four extremal layouts.
  * @author kolja
  *
  */
-public class Combine implements AlgorithmStage {
-
-    LayeredGraphNode graph;
-
-    private enum State
-    {
-        ALIGN,
-        SET_COORDINATES
-    }
-
-    private State state;
-    private int btlOffset;
-    private int btrOffset;
-    private int tblOffset;
-    private int tbrOffset;
-    private int vIndex;
-    private ArrayList< BackwardAction > actions;
-    private PseudoCodeNode alignNode;
-    private PseudoCodeNode setNode;
-    private PseudoCodeNode loopNode;
-    private boolean inside;
-    private boolean insideSubgraph;
-    private boolean breakPoint;
-    private boolean skipBackwards;
-    private ArrayList< PseudoCodeNode > subgraphNodes;
-    private ArrayList< Combine > subgraphAlgs;
-
-    public Combine( LayeredGraphNode graph )
-    {
-        this.graph = graph;
-        state = State.ALIGN;
-        vIndex = 0;
-        actions = new ArrayList<>();
-        inside = false;
-        insideSubgraph = false;
-        skipBackwards = false;
-        subgraphNodes = new ArrayList<>();
-        subgraphAlgs = new ArrayList<>();
-        for( @SuppressWarnings("unused") LayeredGraphNode n : graph.getContainedNodes() )
-        {
-            subgraphAlgs.add( null );
-            subgraphNodes.add( null );
-        }
-    }
-
-    @Override
-    public StageStatus forwardStep() {
-        breakPoint = false;
-        if( state == State.ALIGN )
-        {
-            inside = false;
-            boolean sel = alignNode.isSelected();
-            CodeAction action = alignNode.setSelected( true );
-            if( !sel )
-                breakPoint |= action == CodeAction.STOP;
-            int tblw = (int)calcMaxX( LayoutType.TOP_BOTTOM_LEFT );
-            int tbrw = (int)calcMaxX( LayoutType.TOP_BOTTOM_RIGHT );
-            int btlw = (int)calcMaxX( LayoutType.BOTTOM_TOP_LEFT );
-            int btrw = (int)calcMaxX( LayoutType.BOTTOM_TOP_RIGHT );
-            LayoutType minLayout = LayoutType.TOP_BOTTOM_LEFT;
-            int minWidth = tblw;
-            if( tbrw < minWidth )
+public class Combine {
+
+    public static PseudoCodeNode combine( JTree tree ) {
+        String[] vars = { "graph", "l", "l_min", "v", "positions" };
+        @SuppressWarnings("serial")
+        PseudoCodeNode root = new PseudoCodeNode( "function combine( graph )", vars, tree, new FunctionDefinition( new String[]{ "graph" } ) ){
+            @Override
+            public String getDebugOutput( Memory m )
             {
-                minWidth = tbrw;
-                minLayout = LayoutType.TOP_BOTTOM_RIGHT;
+                if( m.isSomewhereDefined( "v", MemoryType.LOCAL ) )
+                    m.<LayeredGraphNode>read( "v", MemoryType.LOCAL).setSelected( null );
+                return super.getDebugOutput( m );
             }
-            if( btlw < minWidth )
-            {
-                minWidth = btlw;
-                minLayout = LayoutType.BOTTOM_TOP_LEFT;
+        };
+        root.add( new PseudoCodeNode( "-- Call combine of subgraphs --", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode graph = m.read( "graph", MemoryType.LOCAL );
+                if( graph.parent() == null )
+                    graph.setColor( Color.BLACK, null );
+                actions.add( (Memory mem) -> {
+                    if( graph.parent() == null )
+                        graph.setColor( null, null );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
             }
-            if( btrw < minWidth )
-            {
-                minWidth = btrw;
-                minLayout = LayoutType.BOTTOM_TOP_RIGHT;
+        }));
+        PseudoCodeNode recursiveLoop = new PseudoCodeNode( "foreach v in graph do", vars, tree, new ForEachLoop<LayeredGraphNode>( "v" ) {
+            @Override
+            protected List<LayeredGraphNode> list(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedNodes();
             }
-            int minX = calcMinX( minLayout );
-            btlOffset = minX - calcMinX( LayoutType.BOTTOM_TOP_LEFT );
-            tblOffset = minX - calcMinX( LayoutType.TOP_BOTTOM_LEFT );
-            btrOffset = minWidth - btrw;
-            tbrOffset = minWidth - tbrw;
-            Color oldColor = graph.getColor( LayoutType.COMBINED );
-            if( oldColor == null )
-                graph.setColor( Color.BLACK, null );
-            actions.add( 0, () -> {
-                inside = false;
-                loopNode.setSelected( false );
-                setNode.setSelected( false );
-                breakPoint = false;
-                boolean selected = alignNode.isSelected();
-                CodeAction ac = alignNode.setSelected( true );
-                if( !selected )
-                    breakPoint |= ac == CodeAction.STOP;
-                skipBackwards = ac == CodeAction.SKIP;
-                state = State.ALIGN;
-                if( oldColor == null )
-                    graph.setColor( null, null );
-            });
-            state = State.SET_COORDINATES;
-            alignNode.setSelected( false );
-            sel = setNode.isSelected();
-            CodeAction ac = setNode.setSelected( true );
-            boolean skip = ac == CodeAction.SKIP;
-            if( !sel )
-                breakPoint |= ac == CodeAction.STOP;
-            ac = loopNode.setSelected( true );
-            breakPoint |= ac == CodeAction.STOP;
-            skip |= ac == CodeAction.SKIP;
-            if( (action == CodeAction.SKIP || skip) && !breakPoint )
-                return forwardStep();
-        }
-        else
-        {
-            CodeAction action = loopNode.setSelected( true );
-            breakPoint = action == CodeAction.STOP;
-            if( vIndex >= graph.getContainedNodes().size() )
-            {
-                inside = false;
-                setNode.setSelected( false );
-                loopNode.setSelected( false );
-                return StageStatus.FINISHED;
+        });
+        root.add( recursiveLoop );
+        PseudoCodeNode ifNode = new PseudoCodeNode( "if v has subgraph then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getContainedNodes().size() > 0;
             }
-            else
-                inside = true;
-            LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-            current.setSelected( null );
-            if( current.getContainedNodes().size() > 0 )
-            {
-                insideSubgraph = true;
-                boolean breakpoint = false;
-                boolean sel = subgraphNodes.get( vIndex ).isSelected();
-                CodeAction ac = subgraphNodes.get( vIndex ).setSelected( true );
-                if( !sel )
-                    breakpoint |= ac == CodeAction.STOP;
-                do {
-                    switch( subgraphAlgs.get( vIndex ).forwardStep() )
+        } );
+        recursiveLoop.add( ifNode );
+        ifNode.add( new PseudoCodeNode( "call combine( v );", vars, tree, new FunctionCall( root, new String[]{ "v" } ) ) );
+        root.add( new PseudoCodeNode( "-- Align all Layouts to the one with the smallest width --", vars, tree, new Comment()));
+        PseudoCodeNode firstLoop = new PseudoCodeNode( "foreach l in ['DOWN_RIGHT', 'DOWN_LEFT', 'UP_RIGHT', 'UP_LEFT'] do", vars, tree, new ForEachLoop<String>( "l" ) {
+            @Override
+            protected List<String> list(ReadOnlyMemory m) {
+                ArrayList< String > list = new ArrayList<String>();
+                list.add( "DOWN_RIGHT" );
+                list.add( "DOWN_LEFT" );
+                list.add( "UP_RIGHT" );
+                list.add( "UP_LEFT" );
+                return list;
+            }
+        });
+        root.add( firstLoop );
+        firstLoop.add( new PseudoCodeNode( "min[l] = minimum x coordinate in l;", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String layout = m.read( "l", MemoryType.LOCAL );
+                m.declare( "min[" + layout + "]", calcMinX( m.read( "graph", MemoryType.LOCAL ), LayoutType.fromString( layout ) ), MemoryType.GLOBAL );
+                actions.add( (Memory mem) -> {
+                    mem.undeclare( "min[" + layout + "]", MemoryType.GLOBAL );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        firstLoop.add( new PseudoCodeNode( "max[l] = maximum x coordinate in l;", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String layout = m.read( "l", MemoryType.LOCAL );
+                m.declare( "max[" + layout + "]", calcMaxX( m.read( "graph", MemoryType.LOCAL ), LayoutType.fromString( layout ) ), MemoryType.GLOBAL );
+                actions.add( (Memory mem) -> {
+                    mem.undeclare( "max[" + layout + "]", MemoryType.GLOBAL );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        firstLoop.add( new PseudoCodeNode( "width[l] = max[l] - min[l];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String layout = m.read( "l", MemoryType.LOCAL );
+                m.declare( "width[" + layout + "]", m.<Integer>read( "max[" + layout + "]", MemoryType.GLOBAL ) - m.<Integer>read( "min[" + layout + "]", MemoryType.GLOBAL ), MemoryType.GLOBAL );
+                actions.add( (Memory mem) -> {
+                    mem.undeclare( "width[" + layout + "]", MemoryType.GLOBAL );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        root.add( new PseudoCodeNode( "l_min = l with width[l] == min(width);", vars, tree, new DeclareVariable<String>( "l_min" ) {
+            @Override
+            protected String value(ReadOnlyMemory m) {
+                String min = "DOWN_RIGHT";
+                int minW = m.read( "width[DOWN_RIGHT]", MemoryType.GLOBAL );
+                for( String l : new String[]{ "DOWN_LEFT", "UP_RIGHT", "UP_LEFT" } )
+                {
+                    if( minW > m.<Integer>read( "width[" + l + "]", MemoryType.GLOBAL ) )
                     {
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        break;
-                    case FINISHED:
-                        insideSubgraph = false;
-                        subgraphNodes.get( vIndex ).setSelected( false );
-                        ac = null;
-                        break;
+                        minW = m.<Integer>read( "width[" + l + "]", MemoryType.GLOBAL );
+                        min = l;
                     }
-                } while( ac == CodeAction.SKIP && !breakpoint );
+                }
+                return min;
+            }
+        }));
+        PseudoCodeNode secondLoop = new PseudoCodeNode( "foreach l in ['DOWN_RIGHT', 'DOWN_LEFT', 'UP_RIGHT', 'UP_LEFT'] do", vars, tree, new ForEachLoop<String>( "l" ) {
+            @Override
+            protected List<String> list(ReadOnlyMemory m) {
+                ArrayList< String > list = new ArrayList<String>();
+                list.add( "DOWN_RIGHT" );
+                list.add( "DOWN_LEFT" );
+                list.add( "UP_RIGHT" );
+                list.add( "UP_LEFT" );
+                return list;
+            }
+        });
+        root.add( secondLoop );
+        secondLoop.add( new PseudoCodeNode( "shift[l] = l.contains('RIGHT') ? min[l_min] - min[l] : max[l_min] - max[l];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String layout = m.read( "l", MemoryType.LOCAL );
+                String lMin = m.read( "l_min", MemoryType.LOCAL );
+                if( layout.contains( "RIGHT" ) )
+                    m.declare( "shift[" + layout + "]", m.<Integer>read( "min[" + lMin + "]", MemoryType.GLOBAL ) - m.<Integer>read( "min[" + layout + "]", MemoryType.GLOBAL ), MemoryType.GLOBAL );
+                else
+                    m.declare( "shift[" + layout + "]", m.<Integer>read( "max[" + lMin + "]", MemoryType.GLOBAL ) - m.<Integer>read( "max[" + layout + "]", MemoryType.GLOBAL ), MemoryType.GLOBAL );
+                actions.add( (Memory mem) -> {
+                    mem.undeclare( "shift[" + layout + "]", MemoryType.GLOBAL );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        PseudoCodeNode thirdLoop = new PseudoCodeNode( "foreach v in graph do", vars, tree, new ForEachLoop<LayeredGraphNode>( "v" ) {
+            @Override
+            protected List<LayeredGraphNode> list(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedNodes();
+            }
+        });
+        root.add( thirdLoop );
+        thirdLoop.add( new PseudoCodeNode( "positions = [];", vars, tree, new DeclareVariable<ArrayList<Integer>>( "positions") {
+            @Override
+            protected ArrayList<Integer> value(ReadOnlyMemory m) {
+                return new ArrayList<Integer>();
+            }
+        }));
+        PseudoCodeNode innerLoop = new PseudoCodeNode( "foreach l in ['DOWN_RIGHT', 'DOWN_LEFT', 'UP_RIGHT', 'UP_LEFT'] do", vars, tree, new ForEachLoop<String>( "l" ) {
+            @Override
+            protected List<String> list(ReadOnlyMemory m) {
+                ArrayList< String > list = new ArrayList<String>();
+                list.add( "DOWN_RIGHT" );
+                list.add( "DOWN_LEFT" );
+                list.add( "UP_RIGHT" );
+                list.add( "UP_LEFT" );
+                return list;
+            }
+        });
+        innerLoop.add( new PseudoCodeNode( "positions.add(x[v] in l);", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String layout = m.read( "l", MemoryType.LOCAL );
+                ArrayList<Integer> positions = m.read( "positions", MemoryType.LOCAL );
+                positions.add( (int)m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getX( LayoutType.fromString( layout ) ) + m.<Integer>read( "shift[" + layout + "]", MemoryType.GLOBAL ) );
+                actions.add( (Memory mem) -> {
+                    positions.remove( positions.size() - 1 );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        thirdLoop.add( innerLoop );
+        thirdLoop.add( new PseudoCodeNode( "positions = sort( positions );", vars, tree, new SetVariable<ArrayList<Integer>>( "positions" ) {
+            @Override
+            public ArrayList<Integer> value(ReadOnlyMemory m) {
+                ArrayList<Integer> positions = m.read( "positions", MemoryType.LOCAL );
+                ArrayList<Integer> neu = new ArrayList<Integer>();
+                for( int p : positions )
+                    neu.add( p );
+                Collections.sort( neu );
+                return neu;
             }
-            ArrayList< Integer > positions = new ArrayList<>();
-            positions.add( (Integer)(int)current.getX( LayoutType.TOP_BOTTOM_LEFT ) + tblOffset );
-            positions.add( (Integer)(int)current.getX( LayoutType.TOP_BOTTOM_RIGHT ) + tbrOffset );
-            positions.add( (Integer)(int)current.getX( LayoutType.BOTTOM_TOP_LEFT ) + btlOffset );
-            positions.add( (Integer)(int)current.getX( LayoutType.BOTTOM_TOP_RIGHT ) + btrOffset );
-            Collections.sort( positions );
-            int oldX = (int)current.getX( LayoutType.COMBINED );
-            current.setX( (positions.get( 1 ) + positions.get( 2 )) / 2, true, LayoutType.COMBINED );
-            actions.add( 0, () -> {
-                inside = true;
-                breakPoint = false;
-                boolean selected = setNode.isSelected();
-                CodeAction ac = setNode.setSelected( true );
-                if( !selected )
-                    breakPoint |= ac == CodeAction.STOP;
-                skipBackwards = ac == CodeAction.SKIP;
-                ac = loopNode.setSelected( true );
-                breakPoint |= ac == CodeAction.STOP;
-                skipBackwards |= ac == CodeAction.SKIP;
-                vIndex--;
-                current.setX( oldX, true, LayoutType.COMBINED );
-                current.setSelected( null );
-            });
-            vIndex++;
-            if( action == CodeAction.SKIP && !breakPoint )
-                return forwardStep();
-        }
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        return StageStatus.UNFINISHED;
+        }));
+        thirdLoop.add( new PseudoCodeNode( "x[v] = (positions[1]+positions[2]) / 2;", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                ArrayList<Integer> positions = m.read( "positions", MemoryType.LOCAL );
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                double old = v.getX( LayoutType.COMBINED );
+                v.setX( (positions.get( 1 ) + positions.get( 2 )) / 2.0, true, LayoutType.COMBINED );
+                actions.add( (Memory mem) -> {
+                    v.setX( old, false, LayoutType.COMBINED );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        return root;
     }
 
-    private int calcMinX(  LayoutType layout )
+    private static int calcMinX( LayeredGraphNode graph, LayoutType layout )
     {
         int minX = 0;
         if( graph.getContainedNodes().size() > 0 )
@@ -210,7 +232,7 @@ public class Combine implements AlgorithmStage {
         return minX;
     }
 
-    private int calcMaxX(  LayoutType layout )
+    private static int calcMaxX( LayeredGraphNode graph, LayoutType layout )
     {
         int maxX = 0;
         if( graph.getContainedNodes().size() > 0 )
@@ -219,264 +241,4 @@ public class Combine implements AlgorithmStage {
             maxX = Math.max( maxX, (int)n.getX( layout ) );
         return maxX;
     }
-
-    @Override
-    public StageStatus backwardStep() {
-        if( vIndex < subgraphAlgs.size() && subgraphAlgs.get( vIndex ) != null )
-        {
-            insideSubgraph = true;
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( vIndex ).isSelected();
-            CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( vIndex ).backwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-                    current.setSelected( null );
-                    break;
-                case FINISHED:
-                    insideSubgraph = false;
-                    subgraphNodes.get( vIndex ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-        }
-        if( actions.size() == 0 )
-        {
-            inside = false;
-            alignNode.setSelected( false );
-            return StageStatus.FINISHED;
-        }
-        actions.get( 0 ).reverse();
-        actions.remove( 0 );
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        if( skipBackwards )
-            return backwardStep();
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
-        PseudoCodeNode root = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Balancing"), tree );
-        alignNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Align Layouts"), tree );
-        setNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Align to assignment of smallest width"), tree );
-        loopNode = new PseudoCodeNode( "Set coordinates to average median of aligned candidates", tree );
-        for( int i = 0; i < graph.getContainedNodes().size(); i++ )
-        {
-            LayeredGraphNode current = graph.getContainedNodes().get( i );
-            if( current.getContainedNodes().size() > 0 )
-            {
-                Combine extcalc = new Combine( current );
-                PseudoCodeNode subNode = extcalc.createPseudocodeTree( loopNode.getTree() );
-                loopNode.add( subNode );
-                subgraphAlgs.set( i, extcalc );
-                subgraphNodes.set( i, subNode );
-            }
-        }
-        setNode.add( loopNode );
-        root.add( alignNode );
-        root.add( setNode );
-        return root;
-    }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !inside )
-        {
-            State oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = forwardStep();
-            return stage;
-        }
-        else
-        {
-            if( !insideSubgraph )
-                return forwardStep();
-            else
-            {
-                boolean breakpoint = false;
-                boolean selected = subgraphNodes.get( vIndex ).isSelected();
-                CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( subgraphAlgs.get( vIndex ).forwardStepOver() )
-                    {
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        break;
-                    case FINISHED:
-                        insideSubgraph = false;
-                        subgraphNodes.get( vIndex ).setSelected( false );
-                        action = null;
-                        break;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                return StageStatus.UNFINISHED;
-            }
-        }
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-        {
-            if( !insideSubgraph )
-            {
-                State oldState = state;
-                StageStatus stage = StageStatus.UNFINISHED;
-                while( state == oldState && stage == StageStatus.UNFINISHED )
-                    stage = forwardStep();
-                return stage;
-            }
-            else
-            {
-                boolean breakpoint = false;
-                boolean selected = subgraphNodes.get( vIndex ).isSelected();
-                CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( subgraphAlgs.get( vIndex ).forwardStepOut() )
-                    {
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        break;
-                    case FINISHED:
-                        insideSubgraph = false;
-                        subgraphNodes.get( vIndex ).setSelected( false );
-                        action = null;
-                        break;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                return StageStatus.UNFINISHED;
-            }
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !inside )
-        {
-            State oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = backwardStep();
-            return stage;
-        }
-        else
-        {
-            if( !insideSubgraph )
-                return backwardStep();
-            else
-            {
-                boolean breakpoint = false;
-                boolean selected = subgraphNodes.get( vIndex ).isSelected();
-                CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( subgraphAlgs.get( vIndex ).backwardStepOver() )
-                    {
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        break;
-                    case FINISHED:
-                        insideSubgraph = false;
-                        subgraphNodes.get( vIndex ).setSelected( false );
-                        action = null;
-                        break;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                return StageStatus.UNFINISHED;
-            }
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-        {
-            if( !insideSubgraph )
-            {
-                State oldState = state;
-                StageStatus stage = StageStatus.UNFINISHED;
-                while( state == oldState && stage == StageStatus.UNFINISHED )
-                    stage = backwardStep();
-                return stage;
-            }
-            else
-            {
-                boolean breakpoint = false;
-                boolean selected = subgraphNodes.get( vIndex ).isSelected();
-                CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( subgraphAlgs.get( vIndex ).backwardStepOut() )
-                    {
-                    case BREAKPOINT:
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        break;
-                    case FINISHED:
-                        insideSubgraph = false;
-                        subgraphNodes.get( vIndex ).setSelected( false );
-                        action = null;
-                        break;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-                return StageStatus.UNFINISHED;
-            }
-        }
-    }
-    
-    @Override
-    public String getDebugString()
-    {
-        String info = "| Node |  x  | x LU | x RU | x LL | x RL |\n";
-        info +=       "|------|-----|------|------|------|------|\n";
-        for( LayeredGraphNode n : graph.getContainedNodes() )
-        {
-            info += "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getX( LayoutType.COMBINED ) + "", 5 ) + 
-                    "|" + TextLayoutHelper.strToLen( ( n.getX( LayoutType.TOP_BOTTOM_LEFT ) + tblOffset ) + "", 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( ( n.getX( LayoutType.TOP_BOTTOM_RIGHT ) + tbrOffset ) + "", 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( ( n.getX( LayoutType.BOTTOM_TOP_LEFT ) + btlOffset ) + "", 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( ( n.getX( LayoutType.BOTTOM_TOP_RIGHT ) + btrOffset ) + "", 6 ) + "|\n";
-        }
-        if( insideSubgraph && vIndex < graph.getContainedNodes().size() )
-        {
-            info += "Subgraph of " + graph.getContainedNodes().get( vIndex ).getName() + ":\n";
-            String tmp = subgraphAlgs.get( vIndex ).getDebugString();
-            info += tmp;
-            return info;
-        }
-        return info;
-    }
 }

+ 148 - 526
src/bk/Compaction.java

@@ -1,549 +1,171 @@
 package bk;
 
 import java.util.ArrayList;
-import java.util.Collections;
+import java.util.List;
 
 import javax.swing.JTree;
 
-import animation.AlgorithmStage;
-import animation.BackwardAction;
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
-import bk.ExtremalLayoutCalc.LayoutType;
+import codelines.AbstractForLoop;
+import codelines.DeclareVariable;
+import codelines.ForEachLoop;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.IfLoop;
 import graph.LayeredGraphNode;
-import lib.TextLayoutHelper;
 
 /**
  * The stage of compacting the layout.
  * @author kolja
  *
  */
-public class Compaction implements AlgorithmStage{
-
-    private enum CompactionState
-    {
-        PLACE_BLOCKS,
-        APPLY_SHIFT
-    }
-
-    private class StackFrame
-    {
-        public LayeredGraphNode v;
-        public LayeredGraphNode u;
-        public LayeredGraphNode w;
-    }
-
-    private CompactionState state;
-    private LayeredGraphNode graph;
-    private int vIndex;
-
-    private ArrayList< StackFrame > stack; // TODO: evtl richtigen "Stack" benutzen
-    private ArrayList< BackwardAction > actions; // TODO: evtl richtigen "Stack" benutzen
-    private LayoutType layout;
-    private PseudoCodeNode placeNode;
-    private PseudoCodeNode placeLoopNode;
-    private PseudoCodeNode applyNode;
-    private PseudoCodeNode applyLoopNode;
-    private boolean breakPoint;
-    private boolean inside;
-    private boolean skip;
-
-
-    public Compaction( LayeredGraphNode graph, LayoutType layout )
-    {
-        this.layout = layout;
-        this.graph = graph;
-        state = CompactionState.PLACE_BLOCKS;
-        stack = new ArrayList<>(); // der call-stack des rekursiven algorithmus
-        vIndex = 0;
-        actions = new ArrayList<>();
-        inside = false;
-        skip = false;
-    }
-
-    /**
-     * calculates the minimum spacing needed between two left borders of nodes.
-     * @return the spacing
-     */
-    public double calcSpacing()
-    {
-        double max = 0;
-        for( LayeredGraphNode n : graph.getContainedNodes() )
-            max = Math.max( max, n.getWidth( layout ) );
-        //return max + 25;
-        return max + 5;
-    }
-
-    private LayeredGraphNode getNodeFromIndex( int index )
-    {
-        @SuppressWarnings("unchecked")
-        ArrayList<ArrayList<LayeredGraphNode>> layer = (ArrayList<ArrayList<LayeredGraphNode>>) graph.getContainedLayers().clone();
-        if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.BOTTOM_TOP_RIGHT )
-            Collections.reverse( layer );
-        for( ArrayList< LayeredGraphNode > l : layer )
-        {
-            if( index >= l.size() )
-                index -= l.size();
-            else
+public class Compaction {
+
+    public static PseudoCodeNode horizontalCompaction( JTree tree, PseudoCodeNode placeBlock ) {
+        String[] vars = { "i", "L", "layout", "graph", "k", "v" };
+        @SuppressWarnings("serial")
+        PseudoCodeNode root = new PseudoCodeNode( "function horizontalCompaction( layout, graph )", vars, tree, new FunctionDefinition( new String[]{ "layout", "graph" } ) ) {
+            @Override
+            public String getDebugOutput( Memory m )
             {
-                if( layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.TOP_BOTTOM_LEFT )
-                    return l.get( index );
-                else
-                    return l.get( l.size() - index - 1 );
+                if( m.isSomewhereDefined( "v", MemoryType.LOCAL ) )
+                    m.<LayeredGraphNode>read( "v", MemoryType.LOCAL).setSelected( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                return super.getDebugOutput( m );
             }
-        }
-        return null;
-    }
-
-    @Override
-    public StageStatus forwardStep() {
-        breakPoint = false;
-        skip = false;
-        int acSize = actions.size();
-        if( state == CompactionState.PLACE_BLOCKS ) // blöcke platzieren
-        {
-            inside = true;
-            boolean sel = placeNode.isSelected();
-            CodeAction action = placeNode.setSelected( true );
-            if( !sel )
-                breakPoint = action == CodeAction.STOP;
-            skip |= action == CodeAction.SKIP;
-            action = placeLoopNode.setSelected( true );
-            breakPoint |= action == CodeAction.STOP;
-            skip |= action == CodeAction.SKIP;
-            if( stack.size() == 0 ) // äußere schleife, placeblocks bisher nicht aufgerufen
-            {
-                ArrayList< LayeredGraphNode > nodes = graph.getContainedNodes();
-                boolean found = false; // knoten mit v = root[v] gefunden?
-                int oldVIndex = vIndex; // nötig für "undo"
-
-                // suche knoten mit v = root[v] und undefiniertem x-Wert
-                for( ; vIndex < nodes.size(); vIndex++ )
-                {
-                    if( getNodeFromIndex( vIndex ).isXUndefined( layout ) && getNodeFromIndex( vIndex ) == getNodeFromIndex( vIndex ).getRoot( layout ) )
-                    {
-                        found = true;
-                        break;
-                    }
-                }
-
-                // kein knoten gefunden
-                if( !found )
-                {
-                    // wechsele in die phase des Blöckeshiftens
-                    placeNode.setSelected( false );
-                    placeLoopNode.setSelected( false );
-                    sel = applyNode.isSelected();
-                    action = applyNode.setSelected( true );
-                    if( !sel )
-                        breakPoint |= action == CodeAction.STOP;
-                    skip |= action == CodeAction.SKIP;
-                    action = applyLoopNode.setSelected( true );
-                    breakPoint |= action == CodeAction.STOP;
-                    skip |= action == CodeAction.SKIP;
-                    state = CompactionState.APPLY_SHIFT;
-                    inside = false;
-                    vIndex = 0;
-                    actions.add( 0, ()-> {
-                        applyNode.setSelected( false );
-                        applyLoopNode.setSelected( false );
-                        breakPoint = false;
-                        skip = false;
-                        boolean selected = placeNode.isSelected();
-                        CodeAction ac = placeNode.setSelected( true );
-                        if( !selected )
-                            breakPoint |= ac == CodeAction.STOP;
-                        skip |= ac == CodeAction.SKIP;
-                        ac = placeLoopNode.setSelected( true );
-                        breakPoint |= ac == CodeAction.STOP;
-                        skip |= ac == CodeAction.SKIP;
-                        vIndex = oldVIndex;
-                        inside = false;
-                        state = CompactionState.PLACE_BLOCKS;
-                    } );
-                }
-                else // Knoten gefunden
-                {
-                    StackFrame f = new StackFrame(); // enthält lokale variablen
-                    f.v = getNodeFromIndex( vIndex );
-                    double oldX = f.v.getX( layout ); // nötig für "undo"
-                    f.v.setX( 0, true, layout );
-                    f.w = f.v;
-                    f.w.setSelected( layout ); // zeige knoten als aktiven knoten an
-                    stack.add( 0, f );
-
-                    // die "undo"-action
-                    actions.add( 0, ()-> {
-                        breakPoint = false;
-                        skip = false;
-                        boolean selected = placeNode.isSelected();
-                        CodeAction ac = placeNode.setSelected( true );
-                        if( !selected )
-                            breakPoint |= ac == CodeAction.STOP;
-                        skip |= ac == CodeAction.SKIP;
-                        ac = placeLoopNode.setSelected( true );
-                        breakPoint |= ac == CodeAction.STOP;
-                        skip |= ac == CodeAction.SKIP;
-                        inside = true;
-                        stack.get( 0 ).v.setX( oldX, false, layout );
-                        stack.get( 0 ).v.setSelected( layout );
-                        stack.remove( 0 );
-                        vIndex = oldVIndex;
-                        state = CompactionState.PLACE_BLOCKS;
-                    });
-                }
+        };
+        root.add( new PseudoCodeNode( "L = graph.getContainedLayers();", vars, tree, new DeclareVariable<ArrayList<ArrayList<LayeredGraphNode>>>( "L" ) {
+            @Override
+            protected ArrayList<ArrayList<LayeredGraphNode>> value(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers();
             }
-            else // zurzeit innerhalb einer placeblock methode
-            {
-                StackFrame sf = stack.get( 0 );
-                if( sf.u == null ) // zu beginn der placeblock methode
-                {
-                    int posW = graph.getContainedLayers().get( sf.w.getLayer() ).indexOf( sf.w );
-                    if( (posW >= 1 && (layout == LayoutType.BOTTOM_TOP_LEFT || layout == LayoutType.TOP_BOTTOM_LEFT)) || (posW < graph.getContainedLayers().get( sf.w.getLayer() ).size() - 1 && (layout == LayoutType.BOTTOM_TOP_RIGHT || layout == LayoutType.TOP_BOTTOM_RIGHT)) ) // if pos[w] > 1"
-                    {
-                        int offset = -1;
-                        if( layout == LayoutType.BOTTOM_TOP_RIGHT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-                            offset = 1;
-                        sf.u = graph.getContainedLayers().get( sf.w.getLayer() ).get( posW + offset ).getRoot( layout );
-
-                        if( sf.u.isXUndefined( layout ) ) // nötig placeblock aufzurufen?
-                        {// ja
-                            StackFrame nsf = new StackFrame(); // enthält lokale variablen
-                            nsf.v = sf.u;
-                            double oldX = nsf.v.getX( layout ); // nötig für "undo"
-                            nsf.v.setX( 0, true, layout );
-                            nsf.w = nsf.v;
-                            nsf.w.setSelected( layout ); // zeige knoten als aktiven knoten an
-                            stack.add( 0, nsf );
-
-                            // die "undo"-action
-                            actions.add( 0, ()-> {
-                                breakPoint = false;
-                                skip = false;
-                                boolean selected = placeNode.isSelected();
-                                CodeAction ac = placeNode.setSelected( true );
-                                if( !selected )
-                                    breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                ac = placeLoopNode.setSelected( true );
-                                breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                inside = true;
-                                stack.get( 0 ).v.setX( oldX, false, layout );
-                                stack.get( 0 ).v.setSelected( layout );
-                                stack.remove( 0 );
-                                stack.get( 0 ).u = null;
-                            });
-                        }
-                        else // nein
-                        {
-                            // tue nix
-                            sf.w.setSelected( layout );
-                            actions.add( 0, ()-> {
-                                breakPoint = false;
-                                skip = false;
-                                boolean selected = placeNode.isSelected();
-                                CodeAction ac = placeNode.setSelected( true );
-                                if( !selected )
-                                    breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                ac = placeLoopNode.setSelected( true );
-                                breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                inside = true;
-                                stack.get( 0 ).u = null;
-                            });
-                        }
-                    }
-                    else
-                    { // w = align[w]
-                        LayeredGraphNode oldW = sf.w;
-                        sf.w = sf.w.getAlign( layout );
-                        sf.w.setSelected( layout );
-                        if( sf.w == sf.v ) // schleifenabbruchbedingung
-                        { //abbrechen, placeblock beendet
-                            stack.remove( 0 );
-                            actions.add( 0, ()-> {
-                                breakPoint = false;
-                                skip = false;
-                                boolean selected = placeNode.isSelected();
-                                CodeAction ac = placeNode.setSelected( true );
-                                if( !selected )
-                                    breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                ac = placeLoopNode.setSelected( true );
-                                breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                inside = true;
-                                stack.add( 0, sf );
-                                sf.w = oldW;
-                                sf.w.setSelected( layout );
-                            });
-                        }
-                        else
-                        { //nur "undo aktion" hinzufügen
-                            actions.add( 0, ()-> {
-                                breakPoint = false;
-                                skip = false;
-                                boolean selected = placeNode.isSelected();
-                                CodeAction ac = placeNode.setSelected( true );
-                                if( !selected )
-                                    breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                ac = placeLoopNode.setSelected( true );
-                                breakPoint |= ac == CodeAction.STOP;
-                                skip |= ac == CodeAction.SKIP;
-                                inside = true;
-                                sf.w = oldW;
-                                sf.w.setSelected( layout );
-                            });
-                        }
-                    }
-                }
-                else // ein "placeBlock(u)" aufruf hat gerade returned
-                {
-                    // alte Werte merken für undo
-                    LayeredGraphNode oldSink = sf.v.getSink( layout );
-                    LayeredGraphNode sinkOfU = sf.u.getSink( layout );
-                    double oldShift = sinkOfU.getShift( layout );
-                    double oldX = sf.v.getX( layout );
-                    boolean oldDef = !sf.v.isXUndefined( layout );
-
-                    // v für visualisierung markieren
-                    sf.w.setSelected( layout );
-                    
-                    if( sf.v.getSink( layout ) == sf.v ) // sink[v] = v?
-                        sf.v.setSink( sf.u.getSink( layout ), layout ); // sink[v] := sink[u]
-                    int multiplyer = 1;
-                    if( layout == LayoutType.BOTTOM_TOP_RIGHT || layout == LayoutType.TOP_BOTTOM_RIGHT )
-                        multiplyer = -1;
-                    
-                    if( sf.v.getSink( layout ) != sf.u.getSink( layout ) ) // sink[v] != sink [u]?
-                        sf.u.getSink( layout ).setShift( // shift[sink[u]] =
-                          Math.min( sf.u.getSink( layout ).getShift( layout ),  // min(shift[sink[u]]
-                                  multiplyer * (Math.abs(sf.v.getX( layout )) - Math.abs(sf.u.getX( layout )) - calcSpacing()) ), layout ); // y_v - y_u - s
-                    else
-                        // y_v = max {y_v, y_u + s}
-                        sf.v.setX( multiplyer * Math.max( Math.abs( sf.v.getX( layout ) ), Math.abs( sf.u.getX( layout ) ) + calcSpacing() ), true, layout );
-
-
-                    // alte Werte merken für undo
-                    LayeredGraphNode oldW = sf.w;
-                    LayeredGraphNode oldU = sf.u;
-                    
-                    sf.w = sf.w.getAlign( layout ); // w = align[w]
-                    sf.u = null; // u wird nächsten schleifendurchlauf neu gesetzt
-
-                    if( sf.w == sf.v ) // schleifenabbruchbedingung
-                    { //abbrechen, placeblock beendet
-                        stack.remove( 0 );
-                        actions.add( 0, ()-> {
-                            breakPoint = false;
-                            skip = false;
-                            boolean selected = placeNode.isSelected();
-                            CodeAction ac = placeNode.setSelected( true );
-                            if( !selected )
-                                breakPoint |= ac == CodeAction.STOP;
-                            skip |= ac == CodeAction.SKIP;
-                            ac = placeLoopNode.setSelected( true );
-                            breakPoint |= ac == CodeAction.STOP;
-                            skip |= ac == CodeAction.SKIP;
-                            inside = true;
-                            stack.add( 0, sf );
-                            stack.get( 0 ).v.setSink(  oldSink, layout );
-                            sinkOfU.setShift( oldShift, layout );
-                            sf.u = oldU;
-                            sf.v.setX( oldX, oldDef, layout );
-                            sf.w = oldW;
-                            sf.w.setSelected( layout );
-                        });
-                    }
-                    else
-                    { //nur "undo aktion" hinzufügen
-                        actions.add( 0, ()-> {
-                            breakPoint = false;
-                            skip = false;
-                            boolean selected = placeNode.isSelected();
-                            CodeAction ac = placeNode.setSelected( true );
-                            if( !selected )
-                                breakPoint |= ac == CodeAction.STOP;
-                            skip |= ac == CodeAction.SKIP;
-                            ac = placeLoopNode.setSelected( true );
-                            breakPoint |= ac == CodeAction.STOP;
-                            skip |= ac == CodeAction.SKIP;
-                            inside = true;
-                            stack.get( 0 ).v.setSink(  oldSink, layout );
-                            sinkOfU.setShift( oldShift, layout );
-                            sf.u = oldU;
-                            sf.v.setX( oldX, oldDef, layout );
-                            sf.w = oldW;
-                            sf.w.setSelected( layout );
-                        });
-                    }
-                }
+        } ) );
+        PseudoCodeNode firstLoop = new PseudoCodeNode( "for i=layout.contains('DOWN') ? 0 : |L|-1 to layout.contains('DOWN') ? |L|-1 : 0 do", vars, tree, new AbstractForLoop<Integer>( "i" ) {
+            @Override
+            protected Integer begin(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return 0;
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
             }
-        }
-        else if( state == CompactionState.APPLY_SHIFT )// "Compute absolute coordinates"
-        {
-            inside = true;
-            skip = false;
-            boolean sel = placeNode.isSelected();
-            CodeAction action = applyNode.setSelected( true );
-            if( !sel )
-                breakPoint |= action == CodeAction.STOP;
-            skip |= action == CodeAction.SKIP;
-            action = applyLoopNode.setSelected( true );
-            breakPoint |= action == CodeAction.STOP;
-            skip |= action == CodeAction.SKIP;
-            if( vIndex >= graph.getContainedNodes().size() )
-            {
-                inside = false;
-                applyNode.setSelected( false );
-                applyLoopNode.setSelected( false );
-                return StageStatus.FINISHED;
+            @Override
+            protected Integer step(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) + 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) - 1;
             }
-            LayeredGraphNode v = getNodeFromIndex( vIndex );
-            double oldX = v.getX( layout );
-            boolean oldDef = !v.isXUndefined( layout );
-
-            v.setSelected( layout );
-            v.setX( v.getRoot( layout ).getX( layout ), true, layout ); // y_v = y_root[v]
-            if( v == v.getRoot( layout ) && v.getSink( layout ).getShift( layout ) < Double.POSITIVE_INFINITY )
-                v.setX( v.getX( layout ) + v.getSink( layout ).getShift( layout ), true, layout );
-            actions.add( 0, ()-> {
-                inside = true;
-                breakPoint = false;
-                skip = false;
-                boolean selected = placeNode.isSelected();
-                CodeAction ac = applyNode.setSelected( true );
-                if( !selected )
-                    breakPoint |= ac == CodeAction.STOP;
-                skip |= ac == CodeAction.SKIP;
-                ac = applyLoopNode.setSelected( true );
-                breakPoint |= ac == CodeAction.STOP;
-                skip |= ac == CodeAction.SKIP;
-                v.setX( oldX, oldDef, layout );
-                v.setSelected( layout );
-                vIndex--;
-            } );
-            vIndex++;
-            if( vIndex >= graph.getContainedNodes().size() )
-            {
-                applyNode.setSelected( false );
-                applyLoopNode.setSelected( false );
-                return StageStatus.FINISHED;
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) <= m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) >= 0;
             }
-        }
-        if( actions.size() != acSize + 1 )
-            System.out.println( "ERROR: No backwards action was added" );
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        if( skip )
-            return forwardStep();
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public StageStatus backwardStep() {
-        if( actions.size() == 0 )
-        {
-            inside = false;
-            placeNode.setSelected( false );
-            placeLoopNode.setSelected( false );
-            return StageStatus.FINISHED;
-        }
-        actions.get( 0 ).reverse();
-        actions.remove( 0 );
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        if( skip )
-            return backwardStep();
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
-        PseudoCodeNode root = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Horizontal compaction"), tree );
-        placeNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Root coordinates relative to sink"), tree );
-        placeLoopNode = new PseudoCodeNode( "Loop through root nodes...", tree );
-        placeNode.add( placeLoopNode );
-        applyNode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Absolute coordinates"), tree );
-        applyLoopNode = new PseudoCodeNode( "Loop through all nodes...", tree );
-        applyNode.add( applyLoopNode );
-        root.add( placeNode );
-        root.add( applyNode );
+        });
+        root.add( firstLoop );
+        PseudoCodeNode nodeLoop = new PseudoCodeNode( "for k=layout.contains('RIGHT') ? 0 : |L[i]|-1 to layout.contains('RIGHT') ? |L[i]|-1 : 0 do", vars, tree, new AbstractForLoop<Integer>( "k" ) {
+            @Override
+            protected Integer begin( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return 0;
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).size() - 1;
+            }
+            @Override
+            protected Integer step( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return m.<Integer>read( "k", MemoryType.LOCAL ) + 1;
+                return m.<Integer>read( "k", MemoryType.LOCAL ) - 1;
+            }
+            @Override
+            protected boolean condition( ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return m.<Integer>read( "k", MemoryType.LOCAL ) <= m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).size() - 1;
+                return m.<Integer>read( "k", MemoryType.LOCAL ) >= 0;
+            }
+        });
+        firstLoop.add( nodeLoop );
+        nodeLoop.add( new PseudoCodeNode( "v = L[i][k];", vars, tree, new DeclareVariable<LayeredGraphNode>( "v" ) {
+            @Override
+            protected LayeredGraphNode value(ReadOnlyMemory m) {
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).get( m.read( "k", MemoryType.LOCAL ) );
+            }
+        }));
+        PseudoCodeNode ifRoot = new PseudoCodeNode( "if root[v] == v then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getRoot( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) ) == m.<LayeredGraphNode>read( "v", MemoryType.LOCAL );
+            }
+        });
+        nodeLoop.add( ifRoot );
+        ifRoot.add( new PseudoCodeNode( "call place_block(v, layout);", vars, tree, new FunctionCall( placeBlock, new String[]{ "v", "layout" } ) ) );
+        PseudoCodeNode secondLoop = new PseudoCodeNode( "for i=layout.contains('DOWN') ? 0 : |L|-1 to layout.contains('DOWN') ? |L|-1 : 0 do", vars, tree, new AbstractForLoop<Integer>( "i" ) {
+            @Override
+            protected Integer begin(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return 0;
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
+            }
+            @Override
+            protected Integer step(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) + 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) - 1;
+            }
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "DOWN" ) )
+                    return m.<Integer>read( "i", MemoryType.LOCAL ) <= m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 1;
+                return m.<Integer>read( "i", MemoryType.LOCAL ) >= 0;
+            }
+        });
+        root.add( secondLoop );
+        PseudoCodeNode foreach = new PseudoCodeNode( "foreach v in L[i] do", vars, tree, new ForEachLoop<LayeredGraphNode>( "v" ) {
+            @Override
+            protected List<LayeredGraphNode> list(ReadOnlyMemory m) {
+                return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) );
+            }
+        });
+        secondLoop.add( foreach );
+        foreach.add( new PseudoCodeNode( "x[v] = x[root[v]];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) );
+                double old = v.getX( layout );
+                boolean oldDef = !v.isXUndefined( layout );
+                v.setX( v.getRoot( layout ).getX( layout ), true, layout );
+                actions.push( (Memory mem) -> {
+                    v.setX( old, oldDef, layout );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        PseudoCodeNode ifShift = new PseudoCodeNode( "if v == root[v] and ((layout.contains('RIGHT') and shift[sink[v]] < ∞ ) or (layout.contains('LEFT') and shift[sink[v]] > -∞ )) then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                String lStr = m.read( "layout", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( lStr );
+                return v == v.getRoot( layout ) && ((lStr.contains( "RIGHT" ) && v.getSink( layout ).getShift( layout ) < Double.POSITIVE_INFINITY ) || (lStr.contains( "LEFT" ) && v.getSink( layout ).getShift( layout ) > Double.NEGATIVE_INFINITY ));
+            }
+        });
+        foreach.add( ifShift );
+        ifShift.add( new PseudoCodeNode( "x[v] += shift[sink[v]];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) );
+                double old = v.getX( layout );
+                boolean oldDef = !v.isXUndefined( layout );
+                v.setX( old + v.getSink( layout ).getShift( layout ), true, layout );
+                actions.push( (Memory mem) -> {
+                    v.setX( old, oldDef, layout );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
         return root;
     }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !inside )
-        {
-            CompactionState oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = forwardStep();
-            return stage;
-        }
-        else
-            return forwardStep();
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-        {
-            CompactionState oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = forwardStep();
-            return stage;
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !inside )
-        {
-            CompactionState oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = backwardStep();
-            return stage;
-        }
-        else
-            return backwardStep();
-    }
-
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-        {
-            CompactionState oldState = state;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( state == oldState && stage == StageStatus.UNFINISHED )
-                stage = backwardStep();
-            return stage;
-        }
-    }
-    
-    @Override
-    public String getDebugString()
-    {
-        return "";
-    }
 }

+ 221 - 644
src/bk/ConflictDetection.java

@@ -1,14 +1,26 @@
 package bk;
 
 import java.util.ArrayList;
-import java.util.Stack;
+import java.util.List;
 
 import javax.swing.JTree;
 
-import animation.AlgorithmStage;
-import animation.BackwardAction;
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
 import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import bk.LayoutType;
+import codelines.DeclareVariable;
+import codelines.ForEachLoop;
+import codelines.ForLoop;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.IfLoop;
+import codelines.Comment;
+import codelines.SetVariable;
+import codelines.WhileLoop;
 import graph.LayeredGraphEdge;
 import graph.LayeredGraphNode;
 import lib.TextLayoutHelper;
@@ -19,651 +31,216 @@ import lib.TextLayoutHelper;
  * @author kolja and eren
  *
  */
-public class ConflictDetection implements AlgorithmStage {
-
-    private LayeredGraphNode graph;
-    private Stack<BackwardAction> actions;
-
-    private int i;
-    private int l1;
-    private int k0;
-    private int k1;
-    private int l;
-    private int k;
-    private int hidden_k;
-    /**
-     * line number in Carstens' pseudocode listing 3.1
-     */
-    private int pseudo_line;
-    private PseudoCodeNode lines[];
-    private boolean breakPoint;
-    private boolean skip;
-    private ArrayList< PseudoCodeNode > subgraphNodes;
-    private ArrayList< ConflictDetection > subgraphAlgs;
-    private int vIndex;
-    private boolean insideSubgraph;
-
-    public ConflictDetection(LayeredGraphNode graph) {
-        this.graph = graph;
-        actions = new Stack<>();
-        i = 0; // will be increased before first iteration
-        l1 = -1; // will be increased before first iteration
-        k0 = 0;
-        k1 = 0;
-        l = 0;
-        k = 0;
-        hidden_k = 0;
-        pseudo_line = 1;
-        breakPoint = false;
-        skip = false;
-        subgraphAlgs = new ArrayList<>();
-        subgraphNodes = new ArrayList<>();
-        vIndex = 0;
-        insideSubgraph = false;
-        for( @SuppressWarnings("unused") LayeredGraphNode n : graph.getContainedNodes() )
-        {
-            subgraphAlgs.add( null );
-            subgraphNodes.add( null );
-        }
-    }
-
-
-    @Override
-    public StageStatus forwardStep() {
-        int old_line = pseudo_line;
-        int old_k = k;
-        int old_k0 = k0;
-        int old_k1 = k1;
-        int old_l = l;
-        int old_l1 = l1;
-        int old_hidden_k = hidden_k;
-
-        switch (pseudo_line) {
-        case 1:
-            i++;
-            actions.push(() -> {
-                i--;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (i <= graph.getContainedLayers().size() - 2) {
-                pseudo_line++;
-            } else {
-                pseudo_line = 16;
-            }
-            break;
-        case 2:
-            k0 = 0;
-            l = 0;
-            l1 = -1; // will be increased before first iteration
-            pseudo_line++;
-            actions.push(() -> {
-                pseudo_line = old_line;
-                k0 = old_k0;
-                l = old_l;
-                l1 = old_l1;
-                selectNodes(pseudo_line);
-            });
-            break;
-        case 3:
-            l1++;
-            actions.push(() -> {
-                l1--;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (l1 < graph.getContainedLayers().get(i + 1).size()) {
-                pseudo_line += 1;
-            } else {
-                pseudo_line = 1;
-            }
-            break;
-        case 4:
-            actions.push(() -> {
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (graph.getContainedLayers().get(i + 1).size() == l1 || incidentToInnerSegmentBetweenLiPlusOneAndLi()) {
-                pseudo_line++;
-            } else {
-                pseudo_line = 3;
-            }
-            break;
-        case 5:
-            actions.push(() -> {
-                k1 = old_k1;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            k1 = graph.getContainedLayers().get(i).size() - 1;
-            pseudo_line++;
-            break;
-        case 6:
-            actions.push(() -> {
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (incidentToInnerSegmentBetweenLiPlusOneAndLi()) {
-                pseudo_line++;
-            } else {
-                pseudo_line = 9;
-            }
-            break;
-        case 7:
-            actions.push(() -> {
-                k1 = old_k1;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            k1 = graph.getContainedLayers().get(i).indexOf(
-                    graph.getContainedLayers().get(i + 1).get(l1).getSortedIncomingEdges().get(0).getSources().get(0));
-            pseudo_line = 9;
-            break;
-        case 9:
-            actions.push(() -> {
-                hidden_k = old_hidden_k;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (l <= l1) {
-                pseudo_line++;
-                hidden_k = 0; // will not be increased before first iteration
-                k = graph.getContainedLayers().get(i).indexOf(graph.getContainedLayers().get(i + 1).get(l)
-                        .getSortedIncomingEdges().get(hidden_k).getSources().get(0));
-            } else {
-                pseudo_line = 15;
-            }
-            break;
-        case 10:
-            actions.push(() -> {
-                k = old_k;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (hidden_k < graph.getContainedLayers().get(i + 1).get(l).getSortedIncomingEdges().size()) {
-                k = graph.getContainedLayers().get(i).indexOf(graph.getContainedLayers().get(i + 1).get(l)
-                        .getSortedIncomingEdges().get(hidden_k).getSources().get(0));
-                pseudo_line++;
-            } else {
-                pseudo_line = 13;
-            }
-            break;
-        case 11:
-            actions.push(() -> {
-                hidden_k--;
-                graph.getContainedLayers().get(i + 1).get(l).getSortedIncomingEdges().get(hidden_k).setConflicted(false,
-                        null);
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            if (k < k0 || k > k1) {
-                graph.getContainedLayers().get(i + 1).get(l).getSortedIncomingEdges().get(hidden_k).setConflicted(true,
-                        null);
-            }
-            hidden_k++;
-            pseudo_line = 10;
-            break;
-        case 13:
-            actions.push(() -> {
-                l--;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            l++;
-            pseudo_line = 9;
-            break;
-        case 15:
-            actions.push(() -> {
-                k0 = old_k0;
-                pseudo_line = old_line;
-                selectNodes(pseudo_line);
-            });
-            k0 = k1;
-            pseudo_line = 3;
-            break;
-        case 16:
-            if( vIndex < graph.getContainedNodes().size() )
-            {
-                while( vIndex < graph.getContainedNodes().size() && graph.getContainedNodes().get( vIndex ).getContainedNodes().size() == 0 )
-                    vIndex++;
-                if( vIndex < graph.getContainedNodes().size() )
-                {
-                    LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-                    current.setSelected( null );
-                    if( current.getContainedNodes().size() > 0 )
-                    {
-                        insideSubgraph = true;
-                        breakPoint = false;
-                        boolean sel = subgraphNodes.get( vIndex ).isSelected();
-                        CodeAction ac = subgraphNodes.get( vIndex ).setSelected( true );
-                        if( !sel )
-                            breakPoint |= ac == CodeAction.STOP;
-                        do {
-                            switch( subgraphAlgs.get( vIndex ).forwardStep() )
-                            {
-                            case BREAKPOINT:
-                                return StageStatus.BREAKPOINT;
-                            case UNFINISHED:
-                                break;
-                            case FINISHED:
-                                insideSubgraph = false;
-                                subgraphNodes.get( vIndex ).setSelected( false );
-                                vIndex++;
-                                ac = null;
-                                break;
-                            }
-                        } while( ac == CodeAction.SKIP && !breakPoint );
-                    }
-                }
-            }
-            if( vIndex >= graph.getContainedNodes().size() )
-            {
-                for( PseudoCodeNode p : lines )
-                {
-                    if( p != null )
-                        p.setSelected( false );
-                }
-                return StageStatus.FINISHED;
-            }
-        }
-
-        selectNodes(pseudo_line);
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        if( skip )
-            return forwardStep();
-        return StageStatus.UNFINISHED;
-    }
-
-    private void selectNodes(int next_line) {
-        if( pseudo_line < 16 )
-        {
-            if (next_line >= 3 && l < graph.getContainedLayers().get(i + 1).size()) {
-                graph.getContainedLayers().get(i + 1).get(l).setSelected(null);
-            }
-    
-            if (next_line >= 4) {
-                graph.getContainedLayers().get(i + 1).get(l1).setSelected(null);
-            }
-    
-            if (next_line >= 3) {
-                graph.getContainedLayers().get(i).get(k0).setSelected(null);
-            }
+public class ConflictDetection {
     
-            if (next_line >= 6) {
-                graph.getContainedLayers().get(i).get(k1).setSelected(null);
-            }
-    
-            if (next_line == 10 || next_line == 11) {
-                graph.getContainedLayers().get(i).get(k).setSelected(null);
-            }
-        }
-        breakPoint = false;
-        skip = false;
-        for( PseudoCodeNode p : lines )
-        {
-            if( p != null )
-                p.setSelected( false );
-        }
-        markCode( lines[ next_line - 1 ] );
-    }
-    
-    private int getCodeDepth( PseudoCodeNode n )
-    {
-        if( n == lines[ 0 ].getParent() )
-            return 0;
-        return 1 + getCodeDepth( (PseudoCodeNode)n.getParent() );
+    public static String buildDebugString( Memory m ) {
+        if( m.isSomewhereDefined( "l", MemoryType.LOCAL ) && m.isSomewhereDefined( "i", MemoryType.LOCAL ) && 
+                m.<Integer>read( "l", MemoryType.LOCAL ) < m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 ).size() )
+            m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.<Integer>read( "i", MemoryType.LOCAL ) + 1).get(m.<Integer>read( "l", MemoryType.LOCAL )).setSelected(null);
+        
+        if( m.isSomewhereDefined( "i", MemoryType.LOCAL ) && m.isSomewhereDefined( "l1", MemoryType.LOCAL ) &&
+                m.<Integer>read( "l1", MemoryType.LOCAL ) < m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.<Integer>read( "i", MemoryType.LOCAL ) + 1).size() ) {
+            m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.<Integer>read( "i", MemoryType.LOCAL ) + 1).get(m.<Integer>read( "l1", MemoryType.LOCAL )).setSelected(null);
+        }
+        
+        if( m.isSomewhereDefined( "i", MemoryType.LOCAL ) && m.isSomewhereDefined( "k0", MemoryType.LOCAL ) && 
+                m.<Integer>read( "k0", MemoryType.LOCAL ) <  m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.read( "i", MemoryType.LOCAL )).size()) {
+            m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.read( "i", MemoryType.LOCAL )).get(m.<Integer>read( "k0", MemoryType.LOCAL )).setSelected(null);
+        }
+        
+        if( m.isSomewhereDefined( "i", MemoryType.LOCAL ) && m.isSomewhereDefined( "k1", MemoryType.LOCAL ) && 
+                m.<Integer>read( "k1", MemoryType.LOCAL ) < m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.read( "i", MemoryType.LOCAL )).size() ) {
+            m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL).getContainedLayers().get(m.read( "i", MemoryType.LOCAL )).get(m.<Integer>read( "k1", MemoryType.LOCAL )).setSelected(null);
+        }
+        
+        if( m.isSomewhereDefined( "n", MemoryType.LOCAL ) )
+        {
+            m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).setSelected( null );
+        }
+        String info = "| i  | l  | l1 | k0 | k1 |  v  |  n  |\n";
+        info +=       "|----|----|----|----|----|-----|-----|\n";
+        String i = "null";
+        String l = "null";
+        String l1 = "null";
+        String k0 = "null";
+        String k1 = "null";
+        String v = "null";
+        String n = "null";
+        if( m.isSomewhereDefined( "i", MemoryType.LOCAL ) )
+            i = "" + m.<Integer>read( "i", MemoryType.LOCAL );
+        if( m.isSomewhereDefined( "l", MemoryType.LOCAL ) )
+            l = "" + m.<Integer>read( "l", MemoryType.LOCAL );
+        if( m.isSomewhereDefined( "l1", MemoryType.LOCAL ) )
+            l1 = "" + m.<Integer>read( "l1", MemoryType.LOCAL );
+        if( m.isSomewhereDefined( "k0", MemoryType.LOCAL ) )
+            k0 = "" + m.<Integer>read( "k0", MemoryType.LOCAL );
+        if( m.isSomewhereDefined( "k1", MemoryType.LOCAL ) )
+            k1 = "" + m.<Integer>read( "k1", MemoryType.LOCAL );
+        if( m.isSomewhereDefined( "v", MemoryType.LOCAL ) && m.<LayeredGraphEdge>read( "v", MemoryType.LOCAL ).getSources().get( 0 ).getName() != null )
+            v = "" + m.<LayeredGraphEdge>read( "v", MemoryType.LOCAL ).getSources().get( 0 ).getName();
+        if( m.isSomewhereDefined( "n", MemoryType.LOCAL ) && m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getName() != null )
+            n = "" + m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getName();
+        info += "|" + TextLayoutHelper.strToLen( i, 4 ) + 
+                "|" + TextLayoutHelper.strToLen( l, 4 ) + 
+                "|" + TextLayoutHelper.strToLen( l1, 4 ) + 
+                "|" + TextLayoutHelper.strToLen( k0, 4 ) + 
+                "|" + TextLayoutHelper.strToLen( k1, 4 ) + 
+                "|" + TextLayoutHelper.strToLen( v, 5 ) + 
+                "|" + TextLayoutHelper.strToLen( n, 5 ) + "|\n";
+        return info;
     }
     
-    private void markCode( PseudoCodeNode n )
-    {
-        if( n == lines[ 0 ].getRoot() )
-            return;
-        if( !n.hasSelectedSubnode() )
-        {
-            CodeAction action = n.setSelected( true );
-            breakPoint |= action == CodeAction.STOP;
-            skip |= action == CodeAction.SKIP;
-        }
-        else
-        {
-            CodeAction action = n.setSelected( true );
-            skip |= action == CodeAction.SKIP;
-        }
-        if( n == lines[ 0 ] )
-            return;
-        markCode( (PseudoCodeNode)n.getParent() );
-    }
-
-    @Override
-    public StageStatus backwardStep() {
-        if( pseudo_line == 16 )
-        {
-            while( vIndex >= graph.getContainedNodes().size() || ( vIndex >= 0 && graph.getContainedNodes().get( vIndex ).getContainedNodes().size() == 0 )  )
-                vIndex--;
-            if( vIndex >= 0 )
-            {
-                LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-                current.setSelected( null );
-                if( subgraphAlgs.get( vIndex ) != null )
-                {
-                    insideSubgraph = true;
-                    breakPoint = false;
-                    boolean selected = subgraphNodes.get( vIndex ).isSelected();
-                    CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-                    if( !selected )
-                        breakPoint |= action == CodeAction.STOP;
-                    do {
-                        switch( subgraphAlgs.get( vIndex ).backwardStep() )
-                        {
-                        case BREAKPOINT:
-                            return StageStatus.BREAKPOINT;
-                        case UNFINISHED:
-                            break;
-                        case FINISHED:
-                            insideSubgraph = false;
-                            subgraphNodes.get( vIndex ).setSelected( false );
-                            vIndex--;
-                            action = null;
-                            break;
-                        }
-                    } while( !breakPoint && action == CodeAction.SKIP );
-                }
-            }
-            if( vIndex < 0 )
-            {
-                vIndex = 0;
-                if (actions.size() == 0) {
-                    for( PseudoCodeNode p : lines )
-                    {
-                        if( p != null )
-                            p.setSelected( false );
-                    }
-                    return StageStatus.FINISHED;
-                }
-                actions.pop().reverse();
-            }
-        }
-        else
-        {
-            if (actions.size() == 0) {
-                for( PseudoCodeNode p : lines )
-                {
-                    if( p != null )
-                        p.setSelected( false );
-                }
-                return StageStatus.FINISHED;
-            }
-            actions.pop().reverse();
-        }
-        if( breakPoint )
-            return StageStatus.BREAKPOINT;
-        if( skip )
-            return forwardStep();
-        return StageStatus.UNFINISHED;
-    }
-
-    @Override
-    public PseudoCodeNode createPseudocodeTree(JTree tree) {
-        PseudoCodeNode root = new PseudoCodeNode(TextLayoutHelper.setupPseudoCodeStage("Preprocessing (mark type 1 conflicts)"), tree);
-        lines = new PseudoCodeNode[16];
-        String vars[] = { "i", "L", "k0", "l", "l1", "k1", "k", "v" };
-        lines[ 0 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "for i=1 to |L|-2 do", vars ), tree );
-        root.add( lines[ 0 ] );
-        lines[ 1 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k0 = 0; l = 0;", vars ), tree );
-        lines[ 0 ].add( lines[ 1 ] );
-        lines[ 2 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "for l1=0 to |L[i+1]|-1 do", vars ), tree );
-        lines[ 0 ].add( lines[ 2 ] );
-        lines[ 3 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if l1==|L[i+1]|-1 or L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree );
-        lines[ 2 ].add( lines[ 3 ] );
-        lines[ 4 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = |L[i]|-1;", vars ), tree );
-        lines[ 3 ].add( lines[ 4 ] );
-        lines[ 5 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars ), tree );
-        lines[ 3 ].add( lines[ 5 ] );
-        lines[ 6 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k1 = pos(pred(L[i+1][l1])[0]);", vars ), tree );
-        lines[ 5 ].add( lines[ 6 ] );
-        lines[ 8 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "while l <= l1 do", vars ), tree );
-        lines[ 3 ].add( lines[ 8 ] );
-        lines[ 9 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "foreach v in pred(L[i+1][l]) do", vars ), tree );
-        lines[ 8 ].add( lines[ 9 ] );
-        lines[ 10 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "if k < k0 or k > k1 then mark segment (v,L[i+1][l]);", vars ), tree );
-        lines[ 9 ].add( lines[ 10 ] );
-        lines[ 12 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "l = l+1;", vars ), tree );
-        lines[ 8 ].add( lines[ 12 ] );
-        lines[ 14 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCode( "k0 = k1;", vars ), tree );
-        lines[ 3 ].add( lines[ 14 ] );
-        lines[ 15 ] = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage( "mark conflicts in subgraphs" ), tree );
-        root.add( lines[ 15 ] );
-        for( int i = 0; i < graph.getContainedNodes().size(); i++ )
-        {
-            LayeredGraphNode current = graph.getContainedNodes().get( i );
-            if( current.getContainedNodes().size() > 0 )
-            {
-                ConflictDetection extcalc = new ConflictDetection( current );
-                PseudoCodeNode subNode = extcalc.createPseudocodeTree( tree );
-                lines[ 15 ].add( subNode );
-                subgraphAlgs.set( i, extcalc );
-                subgraphNodes.set( i, subNode );
-            }
-        }
+    public static PseudoCodeNode mark_conflicts(JTree tree) {
+        String vars[] = { "i", "L", "k0", "l", "l1", "k1", "v", "graph", "n" };
+        String params[] = { "graph" };
+        PseudoCodeNode root = new PseudoCodeNode( "function mark_conflicts( graph )", vars, tree, new FunctionDefinition( params ) );
+        PseudoCodeNode text = new PseudoCodeNode( "-- mark conflicts in subgraphs --", vars, tree, new Comment() );
+        root.add( text );
+        PseudoCodeNode foreach = new PseudoCodeNode( "foreach n in graph.getContainedNodes() do", vars, tree, new ForEachLoop<LayeredGraphNode>( "n" ) {
+			@Override
+			protected List<LayeredGraphNode> list(ReadOnlyMemory m) {
+				return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedNodes();
+			}
+        } );
+        root.add( foreach );
+        PseudoCodeNode ifNode = new PseudoCodeNode( "if n has subgraph then", vars, tree, new IfLoop() {
+			@Override
+			protected boolean condition(ReadOnlyMemory m) {
+				return m.<LayeredGraphNode>read( "n", MemoryType.LOCAL ).getContainedLayers().size() > 0;
+			}
+        } );
+        foreach.add( ifNode );
+        PseudoCodeNode call = new PseudoCodeNode( "call mark_conflicts( n );", vars, tree, new FunctionCall( root, new String[]{ "n" } ) );
+        ifNode.add( call );
+        text = new PseudoCodeNode( "-- mark conflicts in graph --", vars, tree, new Comment() );
+        root.add( text );
+        PseudoCodeNode init = new PseudoCodeNode( "L = graph.getContainedLayers();", vars, tree, new DeclareVariable<ArrayList<ArrayList<LayeredGraphNode>>>( "L" ) {
+            @Override
+            protected ArrayList<ArrayList<LayeredGraphNode>> value(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers();
+            }
+        } );
+        root.add( init );
+        PseudoCodeNode outerLoop = new PseudoCodeNode( "for i=1 to |L|-2 do", vars, tree, new ForLoop( "i" ) {
+			@Override
+			protected int minimum( ReadOnlyMemory m ) {
+				return 1;
+			}
+			@Override
+			protected int maximum( ReadOnlyMemory m ) {
+				return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).size() - 2;
+			}
+        } );
+        root.add( outerLoop );
+        PseudoCodeNode line = new PseudoCodeNode( "k0 = 0; l = 0;", vars, tree, new CodeLine() {
+			@Override
+			public ControlFlow runForward(Memory m) {
+				m.declare( "k0", 0, MemoryType.LOCAL );
+				m.declare( "l", 0, MemoryType.LOCAL );
+				actions.push( (Memory mem) -> {
+					mem.undeclare( "k0", MemoryType.LOCAL );
+					mem.undeclare( "l", MemoryType.LOCAL );
+				} );
+				return new ControlFlow( ControlFlow.STEP_OVER );
+			}
+        } );
+        outerLoop.add( line );
+        PseudoCodeNode innerLoop = new PseudoCodeNode( "for l1=0 to |L[i+1]|-1 do", vars, tree, new ForLoop( "l1" ) {
+			@Override
+			protected int minimum(ReadOnlyMemory m) {
+				return 0;
+			}
+			@Override
+			protected int maximum(ReadOnlyMemory m) {
+				return m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 ).size() - 1;
+			}
+        } );
+        outerLoop.add( innerLoop );
+        ifNode = new PseudoCodeNode( "if l1==|L[i+1]|-1 or L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars, tree, new IfLoop() {
+			@Override
+			protected boolean condition(ReadOnlyMemory m) {
+	            return m.<Integer>read( "l1", MemoryType.LOCAL ) == m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1).size() - 1 || 
+	            		incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
+			}
+        } );
+        innerLoop.add( ifNode );
+        line = new PseudoCodeNode( "k1 = |L[i]|-1;", vars, tree, new DeclareVariable<Integer>( "k1" ) {
+			@Override
+			protected Integer value(ReadOnlyMemory m) {
+				return (int)m.<ArrayList<ArrayList<LayeredGraphNode>>>read( "L", MemoryType.LOCAL ).get( m.read( "i", MemoryType.LOCAL ) ).size() - 1;
+			}
+        } );
+        ifNode.add( line );
+        PseudoCodeNode innerIfNode = new PseudoCodeNode( "if L[i+1][l1] incident to inner segment between L[i+1] and L[i] then", vars, tree, new IfLoop() {
+			@Override
+			protected boolean condition(ReadOnlyMemory m) {
+				return incidentToInnerSegmentBetweenLiPlusOneAndLi( m );
+			}
+		} );
+        ifNode.add( innerIfNode );
+        
+        line = new PseudoCodeNode( "k1 = pos(pred(L[i+1][l1])[0]);", vars, tree, new SetVariable<Integer>( "k1" ) {
+			@Override
+			protected Integer value(ReadOnlyMemory m) {
+				return (int)m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.read( "i", MemoryType.LOCAL ) ).indexOf(
+						m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 ).get( m.read( "l1", MemoryType.LOCAL ) ).getSortedIncomingEdges().get( 0 ).getSources().get( 0 ) );
+			}
+        } );
+        innerIfNode.add( line );
+        PseudoCodeNode whileLoop = new PseudoCodeNode( "while l <= l1 do", vars, tree, new WhileLoop() {
+			@Override
+			protected boolean condition( ReadOnlyMemory m ) {
+				return m.<Integer>read( "l", MemoryType.LOCAL ) <= m.<Integer>read( "l1", MemoryType.LOCAL );
+			}
+        } );
+        ifNode.add( whileLoop );
+        foreach = new PseudoCodeNode( "foreach v in pred(L[i+1][l]) do", vars, tree, new ForEachLoop<LayeredGraphEdge>( "v" ) {
+			@Override
+			protected List<LayeredGraphEdge> list(ReadOnlyMemory m) {
+				return m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 ).get( m.read( "l", MemoryType.LOCAL ) ).getIncomingEdges();
+			}
+        } );
+        whileLoop.add( foreach );
+        innerIfNode = new PseudoCodeNode( "if pos(v) < k0 or pos(v) > k1 then", vars, tree, new IfLoop() {
+			@Override
+			protected boolean condition(ReadOnlyMemory m) {
+			    int k = m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.read( "i", MemoryType.LOCAL ) ).indexOf( m.<LayeredGraphEdge>read( "v", MemoryType.LOCAL ).getSources().get( 0 ) );
+				return k < m.<Integer>read( "k0", MemoryType.LOCAL ) || k > m.<Integer>read( "k1", MemoryType.LOCAL );
+			}
+        } );
+        foreach.add( innerIfNode );
+        line = new PseudoCodeNode( "mark segment (v,L[i+1][l]);", vars, tree, new CodeLine() {
+			@Override
+			public ControlFlow runForward(Memory m) {
+				LayeredGraphEdge e = m.read( "v", MemoryType.LOCAL );
+				boolean old = e.isConflicted( LayoutType.TOP_BOTTOM_LEFT );
+				e.setConflicted( true, null );
+				actions.add( (Memory mem) -> {
+					e.setConflicted( old, null );
+				});
+				return new ControlFlow( ControlFlow.STEP_OVER );
+			}
+        } );
+        innerIfNode.add( line );
+        line = new PseudoCodeNode( "l = l+1;", vars, tree, new SetVariable<Integer>( "l" ) {
+			@Override
+			protected Integer value(ReadOnlyMemory m) {
+				return (int)m.<Integer>read( "l", MemoryType.LOCAL ) + 1;
+			}
+        } );
+        whileLoop.add( line );
+        line = new PseudoCodeNode( "k0 = k1;", vars, tree, new SetVariable<Integer>( "k0" ) {
+			@Override
+			protected Integer value(ReadOnlyMemory m) {
+				return (int)m.<Integer>read( "k1", MemoryType.LOCAL );
+			}
+        } );
+        ifNode.add( line );
         return root;
     }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !insideSubgraph )
-        {
-            int depth = getCodeDepth( lines[ pseudo_line - 1 ] );
-            do
-            {
-                switch( forwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case FINISHED:
-                    return StageStatus.FINISHED;
-                case UNFINISHED:
-                    break;
-                }
-            } while( depth < getCodeDepth( lines[ pseudo_line - 1 ] ) );
-            return StageStatus.UNFINISHED;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( vIndex ).isSelected();
-            CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( vIndex ).forwardStepOver() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    insideSubgraph = false;
-                    subgraphNodes.get( vIndex ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !insideSubgraph )
-        {
-            int depth = getCodeDepth( lines[ pseudo_line - 1 ] );
-            do
-            {
-                switch( forwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case FINISHED:
-                    return StageStatus.FINISHED;
-                case UNFINISHED:
-                    break;
-                }
-            } while( depth <= getCodeDepth( lines[ pseudo_line - 1 ] ) );
-            return StageStatus.UNFINISHED;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( vIndex ).isSelected();
-            CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( vIndex ).forwardStepOut() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    insideSubgraph = false;
-                    subgraphNodes.get( vIndex ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !insideSubgraph )
-        {
-            int depth = getCodeDepth( lines[ pseudo_line - 1 ] );
-            do
-            {
-                switch( backwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case FINISHED:
-                    return StageStatus.FINISHED;
-                case UNFINISHED:
-                    break;
-                }
-            } while( depth < getCodeDepth( lines[ pseudo_line - 1 ] ) );
-            return StageStatus.UNFINISHED;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( vIndex ).isSelected();
-            CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( vIndex ).backwardStepOver() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-                    current.setSelected( null );
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    insideSubgraph = false;
-                    subgraphNodes.get( vIndex ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !insideSubgraph )
-        {
-            int depth = getCodeDepth( lines[ pseudo_line - 1 ] );
-            do
-            {
-                switch( backwardStep() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case FINISHED:
-                    return StageStatus.FINISHED;
-                case UNFINISHED:
-                    break;
-                }
-            } while( depth <= getCodeDepth( lines[ pseudo_line - 1 ] ) );
-            return StageStatus.UNFINISHED;
-        }
-        else
-        {
-            boolean breakpoint = false;
-            boolean selected = subgraphNodes.get( vIndex ).isSelected();
-            CodeAction action = subgraphNodes.get( vIndex ).setSelected( true );
-            if( !selected )
-                breakpoint |= action == CodeAction.STOP;
-            do {
-                switch( subgraphAlgs.get( vIndex ).backwardStepOut() )
-                {
-                case BREAKPOINT:
-                    return StageStatus.BREAKPOINT;
-                case UNFINISHED:
-                    LayeredGraphNode current = graph.getContainedNodes().get( vIndex );
-                    current.setSelected( null );
-                    if( breakpoint )
-                        return StageStatus.BREAKPOINT;
-                    return StageStatus.UNFINISHED;
-                case FINISHED:
-                    insideSubgraph = false;
-                    subgraphNodes.get( vIndex ).setSelected( false );
-                    action = null;
-                    break;
-                }
-            } while( !breakpoint && action == CodeAction.SKIP );
-            return StageStatus.UNFINISHED;
-        }
-    }
-
-    @Override
-    public String getDebugString() {
-        String info = "| i  | l  | l1 | k0 | k1 | k  |\n";
-        info +=       "|----|----|----|----|----|----|\n";
-        info += "|" + TextLayoutHelper.strToLen( "" + i, 4 ) + 
-                "|" + TextLayoutHelper.strToLen( "" + l, 4 ) + 
-                "|" + TextLayoutHelper.strToLen( "" + l1, 4 ) + 
-                "|" + TextLayoutHelper.strToLen( "" + k0, 4 ) + 
-                "|" + TextLayoutHelper.strToLen( "" + k1, 4 ) + 
-                "|" + TextLayoutHelper.strToLen( "" + k, 4 ) + "|\n";
-        if( insideSubgraph && vIndex < graph.getContainedNodes().size() )
-        {
-            info += "Subgraph of " + graph.getContainedNodes().get( vIndex ).getName() + ":\n";
-            String tmp = subgraphAlgs.get( vIndex ).getDebugString();
-            info += tmp;
-            return info;
-        }
-        return info;
-    }
-
-    /**
-     * part of line 4 and 6 in the pseudocode
-     * 
-     * @return
-     */
-    private boolean incidentToInnerSegmentBetweenLiPlusOneAndLi() {
-        LayeredGraphNode curr = graph.getContainedLayers().get(i + 1).get(l1);
+    
+    private static boolean incidentToInnerSegmentBetweenLiPlusOneAndLi( ReadOnlyMemory m ) {
+        LayeredGraphNode curr = m.<LayeredGraphNode>read( "graph", MemoryType.LOCAL ).getContainedLayers().get( m.<Integer>read( "i", MemoryType.LOCAL ) + 1 ).get( m.read( "l1", MemoryType.LOCAL ) );
         for (LayeredGraphEdge e : curr.getIncomingEdges()) {
             if (e.isDummyEdge()) {
                 return true;

+ 0 - 291
src/bk/ExtremalLayoutCalc.java

@@ -1,291 +0,0 @@
-package bk;
-
-import java.lang.reflect.InvocationTargetException;
-
-import javax.swing.JTree;
-
-import animation.AlgorithmStage;
-import animation.PseudoCodeNode;
-import animation.PseudoCodeNode.CodeAction;
-import graph.LayeredGraphNode;
-import lib.TextLayoutHelper;
-
-/**
- * The stage where the for extremal layouts are computed.
- * @author kolja
- *
- */
-public class ExtremalLayoutCalc implements AlgorithmStage {
-    /**
-     * There are four types of layouts, based on iteration order and median alignment.
-     * Additionally there is one layout for the combined coordinates.
-     * @author kolja
-     *
-     */
-    public enum LayoutType{
-        TOP_BOTTOM_LEFT,
-        TOP_BOTTOM_RIGHT,
-        BOTTOM_TOP_LEFT,
-        BOTTOM_TOP_RIGHT,
-        COMBINED
-    }
-
-    private enum LayoutState
-    {
-        BLOCK_CALCULATION,
-        COMPACTION
-    }
-
-    private PseudoCodeNode pseudoCode;
-    private BlockCalc bc;
-    private Compaction cp;
-    private LayoutState status;
-    private PseudoCodeNode bcNode;
-    private PseudoCodeNode cpNode;
-    private LayoutType type;
-    private boolean inside;
-    private LayeredGraphNode graph;
-
-    public ExtremalLayoutCalc( LayoutType typ, LayeredGraphNode graph )
-    {
-        this.graph = graph;
-        type = typ;
-        status = LayoutState.BLOCK_CALCULATION;
-        bc = new BlockCalc( graph, typ );
-        cp = new Compaction( graph, typ );
-        inside = false;
-    }
-
-    @Override
-    public StageStatus forwardStep() {
-        return forward( "forwardStep" );
-    }
-
-    @Override
-    public StageStatus backwardStep() {
-        return backward( "backwardStep" );
-    }
-
-    @Override
-    public PseudoCodeNode createPseudocodeTree( JTree tree ) {
-        if( type == LayoutType.TOP_BOTTOM_LEFT )
-            pseudoCode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Extremal layout: leftmost upper"), tree );
-        if( type == LayoutType.TOP_BOTTOM_RIGHT )
-            pseudoCode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Extremal layout: rightmost upper"), tree );
-        if( type == LayoutType.BOTTOM_TOP_LEFT )
-            pseudoCode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Extremal layout: leftmost lower"), tree );;
-        if( type == LayoutType.BOTTOM_TOP_RIGHT )
-            pseudoCode = new PseudoCodeNode( TextLayoutHelper.setupPseudoCodeStage("Extremal layout: rightmost lower"), tree );
-        bcNode = bc.createPseudocodeTree( tree );
-        cpNode = cp.createPseudocodeTree( tree );
-        pseudoCode.add( bcNode );
-        pseudoCode.add( cpNode );
-        return pseudoCode;
-    }
-
-    @Override
-    public StageStatus forwardStepOver() {
-        if( !inside )
-        {
-            LayoutState oldState = status;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( status == oldState && stage == StageStatus.UNFINISHED )
-                stage = forwardStep();
-            return stage;
-        }
-        else
-            return forward( "forwardStepOver" );
-    }
-
-    @Override
-    public StageStatus forwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = forwardStep();
-            return status;
-        }
-        else
-            return forward( "forwardStepOut" );
-    }
-
-    @Override
-    public StageStatus backwardStepOver() {
-        if( !inside )
-        {
-            LayoutState oldState = status;
-            StageStatus stage = StageStatus.UNFINISHED;
-            while( status == oldState && stage == StageStatus.UNFINISHED )
-                stage = backwardStep();
-            return stage;
-        }
-        else
-            return backward( "backwardStepOver" );
-    }
-
-    @Override
-    public StageStatus backwardStepOut() {
-        if( !inside )
-        {
-            StageStatus status = StageStatus.UNFINISHED;
-            while( status == StageStatus.UNFINISHED )
-                status = backwardStep();
-            return status;
-        }
-        else
-            return backward( "backwardStepOut" );
-    }
-
-    @Override
-    public String getDebugString()
-    {
-        String info = "| Node | Shift | Sink | Root | Align |  x  |  xDef  |\n";
-        info +=       "|------|-------|------|------|-------|-----|--------|\n";
-        for( LayeredGraphNode n : graph.getContainedNodes() )
-        {
-            info += "|" + TextLayoutHelper.strToLen( n.getName(), 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getShift( type ) + "", 7 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getSink( type ).getName(), 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getRoot( type ).getName(), 6 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getAlign( type ).getName(), 7 ) + 
-                    "|" + TextLayoutHelper.strToLen( n.getX( type ) + "", 5 ) + 
-                    "|" + TextLayoutHelper.strToLen( !n.isXUndefined( type ) + "", 8 ) + "|\n";
-        }
-        if( inside && status == LayoutState.BLOCK_CALCULATION )
-            info += bc.getDebugString();
-        return info;
-    }
-    
-    private StageStatus forward( String fName )
-    {
-        boolean breakpoint = false;
-        CodeAction action = null;
-        try {
-            if( status == LayoutState.BLOCK_CALCULATION )
-            {
-                boolean selected = bcNode.isSelected();
-                action = bcNode.setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( (StageStatus)(BlockCalc.class.getMethod( fName ).invoke( bc ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        bcNode.setSelected( false );
-                        CodeAction ac = cpNode.setSelected( true );
-                        status = LayoutState.COMPACTION;
-                        if( ac == CodeAction.SKIP && !breakpoint )
-                            return forward( fName );
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-            }
-            else if( status == LayoutState.COMPACTION )
-            {
-                boolean selected = cpNode.isSelected();
-                action = cpNode.setSelected( true );
-                if( !selected )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( (StageStatus)(Compaction.class.getMethod( fName ).invoke( cp ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        cpNode.setSelected( false );
-                        return StageStatus.FINISHED;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-            }
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (IllegalArgumentException e) {
-            e.printStackTrace();
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        } catch (SecurityException e) {
-            e.printStackTrace();
-        }
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        return StageStatus.UNFINISHED;
-    }
-    
-    private StageStatus backward( String fName )
-    {
-        boolean breakpoint = false;
-        CodeAction action = null;
-        try {
-            if( status == LayoutState.BLOCK_CALCULATION )
-            {
-                boolean sel = bcNode.isSelected();
-                action = bcNode.setSelected( true );
-                if( !sel )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( (StageStatus)(BlockCalc.class.getMethod( fName ).invoke( bc ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        bcNode.setSelected( false );
-                        return StageStatus.FINISHED;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-            }
-            else if( status == LayoutState.COMPACTION )
-            {
-                boolean sel = cpNode.isSelected();
-                action = cpNode.setSelected( true );
-                if( !sel )
-                    breakpoint |= action == CodeAction.STOP;
-                do {
-                    switch( (StageStatus)(Compaction.class.getMethod( fName ).invoke( cp ) ) )
-                    {
-                    case FINISHED:
-                        inside = false;
-                        cpNode.setSelected( false );
-                        status = LayoutState.BLOCK_CALCULATION;
-                        action = null;
-                        break;
-                    case BREAKPOINT:
-                        inside = true;
-                        return StageStatus.BREAKPOINT;
-                    case UNFINISHED:
-                        inside = true;
-                    }
-                } while( !breakpoint && action == CodeAction.SKIP );
-            }
-        } catch (IllegalAccessException e) {
-            e.printStackTrace();
-        } catch (IllegalArgumentException e) {
-            e.printStackTrace();
-        } catch (InvocationTargetException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
-        } catch (SecurityException e) {
-            e.printStackTrace();
-        }
-        if( breakpoint )
-            return StageStatus.BREAKPOINT;
-        return StageStatus.UNFINISHED;
-    }
-}

+ 30 - 0
src/bk/LayoutType.java

@@ -0,0 +1,30 @@
+package bk;
+
+/**
+ * There are four types of layouts, based on iteration order and median alignment.
+ * Additionally there is one layout for the combined coordinates.
+ * @author kolja
+ *
+ */
+public enum LayoutType{
+    TOP_BOTTOM_LEFT,
+    TOP_BOTTOM_RIGHT,
+    BOTTOM_TOP_LEFT,
+    BOTTOM_TOP_RIGHT,
+    COMBINED;
+    
+    public static LayoutType fromString( String s )
+    {
+        switch( s ) {
+        case "DOWN_RIGHT":
+            return TOP_BOTTOM_LEFT;
+        case "DOWN_LEFT":
+            return TOP_BOTTOM_RIGHT;
+        case "UP_RIGHT":
+            return BOTTOM_TOP_LEFT;
+        case "UP_LEFT":
+            return BOTTOM_TOP_RIGHT;
+        }
+        return null;
+    }
+}

+ 180 - 0
src/bk/PlaceBlock.java

@@ -0,0 +1,180 @@
+package bk;
+
+import javax.swing.JTree;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.PseudoCodeNode;
+import codelines.DeclareVariable;
+import codelines.FunctionCall;
+import codelines.FunctionDefinition;
+import codelines.IfLoop;
+import codelines.SetVariable;
+import codelines.WhileLoop;
+import graph.LayeredGraphNode;
+// PAPER = OUR
+
+// DOWN = RIGHT
+// UP = LEFT
+// RIGHT = DOWN
+// LEFT = UP
+public class PlaceBlock{
+
+    public static PseudoCodeNode place_block(JTree tree) {
+        String vars[] = { "v", "w", "layout", "x", "y", "graph", "u", "root", "align", "first" };
+        @SuppressWarnings("serial")
+        PseudoCodeNode root = new PseudoCodeNode( "function place_block( v, layout )", vars, tree, new FunctionDefinition(new String[]{ "v", "layout" } ) ) {
+            @Override
+            public String getDebugOutput( Memory m )
+            {
+                if( m.isSomewhereDefined( "v", MemoryType.LOCAL ) && !m.isSomewhereDefined( "w", MemoryType.LOCAL ) )
+                    m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).setSelected( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                if( m.isSomewhereDefined( "w", MemoryType.LOCAL ) )
+                    m.<LayeredGraphNode>read( "w", MemoryType.LOCAL ).setSelected( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                return super.getDebugOutput( m );
+            }
+        };
+        PseudoCodeNode ifUndef = new PseudoCodeNode( "if x[v] == undefined then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).isXUndefined( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+            }
+        });
+        root.add( ifUndef );
+        ifUndef.add( new PseudoCodeNode( "x[v] = 0; w = v; first = true;", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                m.declare( "w", m.read( "v", MemoryType.LOCAL ), MemoryType.LOCAL );
+                m.declare( "first", true, MemoryType.LOCAL );
+                double oldX = m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getX( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).setX( 0, true, LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                actions.push( (Memory mem) -> {
+                    mem.undeclare( "w", MemoryType.LOCAL );
+                    mem.undeclare( "first", MemoryType.LOCAL );
+                    m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).setX( oldX, false, LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        PseudoCodeNode whilewv = new PseudoCodeNode( "while w != v or first", vars, tree, new WhileLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                return m.read( "w", MemoryType.LOCAL ) != m.read( "v", MemoryType.LOCAL ) || m.<Boolean>read( "first", MemoryType.LOCAL );
+            }
+        });
+        ifUndef.add( whilewv );
+        whilewv.add( new PseudoCodeNode( "first = false;", vars, tree, new SetVariable<Boolean>( "first" ) {
+            @Override
+            protected Boolean value(ReadOnlyMemory m) {
+                return false;
+            }
+        }));
+        PseudoCodeNode ifPos = new PseudoCodeNode( "if (layout.contains('RIGHT') and pos(w)>0) or (layout.contains('LEFT') and pos(w)<|layerOf(w)|-1) then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayeredGraphNode w = m.read( "w", MemoryType.LOCAL );
+                LayeredGraphNode graph = w.parent();
+                return (m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) && graph.getContainedLayers().get( w.getLayer() ).indexOf( w ) > 0 ) ||
+                       (m.<String>read( "layout", MemoryType.LOCAL ).contains( "LEFT" ) && graph.getContainedLayers().get( w.getLayer() ).indexOf( w ) < graph.getContainedLayers().get( w.getLayer() ).size() - 1 );
+            }
+        });
+        whilewv.add( ifPos );
+        ifPos.add( new PseudoCodeNode( "u = layout.contains('RIGHT') ? root[fore(w)] : root[foll(w)];", vars, tree, new DeclareVariable<LayeredGraphNode>( "u" ){
+            @Override
+            protected LayeredGraphNode value(ReadOnlyMemory m) {
+                LayeredGraphNode w = m.read( "w", MemoryType.LOCAL );
+                LayeredGraphNode graph = w.parent();
+                if( m.<String>read( "layout", MemoryType.LOCAL ).contains( "RIGHT" ) )
+                    return graph.getContainedLayers().get( w.getLayer() ).get( graph.getContainedLayers().get( w.getLayer() ).indexOf( w ) - 1 ).getRoot( LayoutType.fromString( m.<String>read( "layout", MemoryType.LOCAL ) ) );
+                return graph.getContainedLayers().get( w.getLayer() ).get( graph.getContainedLayers().get( w.getLayer() ).indexOf( w ) + 1 ).getRoot( LayoutType.fromString( m.<String>read( "layout", MemoryType.LOCAL ) ) );
+            }
+        } ));
+        ifPos.add( new PseudoCodeNode( "call place_block( u, layout );", vars, tree, new FunctionCall( root, new String[]{ "u", "layout" } ) ) );
+        ifPos.add( new PseudoCodeNode( "sink[v] = sink[v] == v ? sink[u] : sink[v];", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                LayeredGraphNode u = m.read( "u", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) );
+                LayeredGraphNode old = v.getSink( layout );
+                v.setSink( old == v ? u.getSink( layout ) : old, layout );
+                actions.push( (Memory mem) -> {
+                   v.setSink( old, layout ); 
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        PseudoCodeNode ifSink1 = new PseudoCodeNode( "if sink[v] != sink[u] then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayoutType layout = LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) );
+                return m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getSink( layout ) != m.<LayeredGraphNode>read( "u", MemoryType.LOCAL ).getSink( layout );
+            }
+        });
+        ifPos.add( ifSink1 );
+        ifSink1.add( new PseudoCodeNode( "shift[sink[u]] = layout.contains('RIGHT') ? min(shift[sink[u]], x[v] - x[u] - spacing) : max(shift[sink[u]], x[v] + x[u] + spacing);", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String lStr = m.read( "layout", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( lStr );
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                LayeredGraphNode u = m.read( "u", MemoryType.LOCAL );
+                double old = u.getSink( layout ).getShift( layout );
+                u.getSink( layout ).setShift( lStr.contains( "RIGHT" ) ? 
+                        Math.min( u.getSink( layout ).getShift( layout ), v.getX( layout ) - u.getX( layout ) - calcSpacing( u.parent(), layout ) ) : 
+                            Math.max( u.getSink( layout ).getShift( layout ), v.getX( layout ) + u.getX( layout ) + calcSpacing( u.parent(), layout ) ), layout);
+                actions.push( (Memory mem) -> {
+                    u.getSink( layout ).setShift( old, layout );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        PseudoCodeNode ifSink2 = new PseudoCodeNode( "if sink[v] == sink[u] then", vars, tree, new IfLoop() {
+            @Override
+            protected boolean condition(ReadOnlyMemory m) {
+                LayoutType layout = LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) );
+                return m.<LayeredGraphNode>read( "v", MemoryType.LOCAL ).getSink( layout ) == m.<LayeredGraphNode>read( "u", MemoryType.LOCAL ).getSink( layout );
+            }
+        });
+        ifPos.add( ifSink2 );
+        ifSink2.add( new PseudoCodeNode( "x[v] = layout.contains('RIGHT') ? max(x[v], x[u] + spacing) : max(x[v], x[u] - spacing);", vars, tree, new CodeLine() {
+            @Override
+            public ControlFlow runForward(Memory m) {
+                String lStr = m.read( "layout", MemoryType.LOCAL );
+                LayoutType layout = LayoutType.fromString( lStr );
+                LayeredGraphNode v = m.read( "v", MemoryType.LOCAL );
+                LayeredGraphNode u = m.read( "u", MemoryType.LOCAL );
+                double old = v.getX( layout );
+                boolean oldDef = !v.isXUndefined( layout );
+                v.setX( lStr.contains( "RIGHT" ) ? Math.max( v.getX( layout ), u.getX( layout ) + calcSpacing( u.parent(), layout ) ) : Math.min( v.getX( layout ), u.getX( layout ) - calcSpacing( u.parent(), layout ) ), true, layout );
+                actions.push( (Memory mem) -> {
+                    v.setX( old, oldDef, layout );
+                });
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+        }));
+        whilewv.add( new PseudoCodeNode( "w = align[w];", vars, tree, new SetVariable<LayeredGraphNode>( "w" ) {
+            @Override
+            protected LayeredGraphNode value(ReadOnlyMemory m) {
+                return m.<LayeredGraphNode>read( "w", MemoryType.LOCAL ).getAlign( LayoutType.fromString( m.read( "layout", MemoryType.LOCAL ) ) );
+            }
+        }));
+        return root;
+    }
+
+
+    /**
+     * calculates the minimum spacing needed between two left borders of nodes.
+     * @return the spacing
+     */
+    public static double calcSpacing( LayeredGraphNode graph, LayoutType layout )
+    {
+        double max = 0;
+        for( LayeredGraphNode n : graph.getContainedNodes() )
+            max = Math.max( max, n.getWidth( layout ) );
+        return max + 5;
+    }
+}

+ 65 - 0
src/codelines/AbstractForLoop.java

@@ -0,0 +1,65 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.StackFrame.FrameType;
+
+public abstract class AbstractForLoop<T> extends CodeLine {
+
+    String loopVar;
+
+    public AbstractForLoop( String varName )
+    {
+        this.loopVar = varName;
+    }
+    
+    @Override
+    public ControlFlow runForward(Memory m) {
+        if( !m.isDefined( loopVar, MemoryType.LOCAL ) )
+        { // first loop step
+            m.addFrame( new StackFrame( FrameType.LOOP ) );
+            m.declare( loopVar, begin( m.createReadOnlyMemory() ), MemoryType.LOCAL ); // set loop variable
+            if( !condition( m.createReadOnlyMemory() ) ) // prove if the loop has finished
+            {
+                m.removeFrame();
+                actions.push( (Memory mem) -> {} );
+                return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
+            }
+            actions.push( (Memory mem) -> {
+                mem.removeFrame();
+            } );
+            return new ControlFlow( ControlFlow.STEP_INTO );
+        }
+        T next = step( m.createReadOnlyMemory() );
+        T old = m.read( loopVar, MemoryType.LOCAL );
+        m.write( loopVar, next, MemoryType.LOCAL );
+        if( !condition( m.createReadOnlyMemory() ) ) // prove if loop was finished
+        {
+            StackFrame sf = m.removeFrame(); // remove loop stack
+            actions.add( (Memory mem) -> {
+                mem.addFrame( sf ); // restore last loop stack
+                mem.write( loopVar, old, MemoryType.LOCAL );
+            });
+            return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+        }
+        StackFrame oldF = m.removeFrame(); // fresh stack frame for loop body
+        m.addFrame( new StackFrame( FrameType.LOOP ) );
+        m.declare( loopVar, next, MemoryType.LOCAL );
+        actions.push( (Memory mem) -> {
+            mem.removeFrame();
+            mem.addFrame( oldF );
+            mem.write( loopVar, old, MemoryType.LOCAL );
+        });
+        return new ControlFlow( ControlFlow.STEP_INTO );
+    }
+    
+    abstract protected T begin( ReadOnlyMemory m );
+    
+    abstract protected T step( ReadOnlyMemory m );
+    
+    abstract protected boolean condition( final ReadOnlyMemory m );
+}

+ 74 - 0
src/codelines/BackwardForEachLoop.java

@@ -0,0 +1,74 @@
+package codelines;
+
+import java.util.List;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.StackFrame.FrameType;
+
+public class BackwardForEachLoop <T> extends CodeLine {
+
+	String listVar;
+	String loopVar;
+	
+	public BackwardForEachLoop( String listName, String varName )
+	{
+		this.listVar = listName;
+		this.loopVar = varName;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		boolean declared = false; // prove if it is the first step in the loop
+		if( !m.isSomewhereDefined( "line_" + lineId + "_index", MemoryType.LOCAL ) )
+		{ // first loop step
+			m.declare( "line_" + lineId + "_index", m.<List<T>>read( listVar, MemoryType.LOCAL ).size() - 1, MemoryType.LOCAL );
+			declared = true;
+		}
+		if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) < 0 ) // prove if the loop has finished
+		{
+			m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			actions.push( (Memory mem) -> {});
+			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
+		}
+		if( declared )
+		{
+			m.declare( loopVar, m.<List<T>>read( listVar, MemoryType.LOCAL ).get( m.read( "line_" + lineId + "_index", MemoryType.LOCAL ) ), MemoryType.LOCAL ); // set loop variable
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			} );
+		}
+		else
+		{
+			int oldIndex = m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL );
+			m.write( "line_" + lineId + "_index", oldIndex - 1, MemoryType.LOCAL ); // count index down
+			if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) < 0 ) // prove if loop was finished
+			{
+				StackFrame sf = m.removeFrame(); // remove loop stack
+                T old = m.read( loopVar, MemoryType.LOCAL );
+                m.undeclare( loopVar, MemoryType.LOCAL );
+				m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+				actions.push( (Memory mem) -> {
+	                m.declare( loopVar, old, MemoryType.LOCAL );
+					mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+					mem.addFrame( sf ); // restore last loop stack
+				});
+				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+			}
+			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			m.write( loopVar, m.<List<T>>read( listVar, MemoryType.LOCAL ).get( m.read( "line_" + lineId + "_index", MemoryType.LOCAL ) ), MemoryType.LOCAL ); // update loop variable
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.addFrame( old );
+				mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+			});
+		}
+		return new ControlFlow( ControlFlow.STEP_INTO );
+	}
+}

+ 12 - 0
src/codelines/Comment.java

@@ -0,0 +1,12 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+
+public class Comment extends CodeLine{
+	@Override
+	public ControlFlow runForward(Memory m) {
+		return new ControlFlow( ControlFlow.STEP_OVER ); // just do nothing
+	}
+}

+ 42 - 0
src/codelines/DeclareVariable.java

@@ -0,0 +1,42 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+
+public abstract class DeclareVariable <T> extends CodeLine {
+
+	private boolean oldExists;
+	private String name;
+	private MemoryType mt;
+    
+    public DeclareVariable( String name )
+    {
+        this.name = name;
+        this.mt = MemoryType.LOCAL;
+    }
+	
+	public DeclareVariable( String name, MemoryType mt )
+	{
+		this.name = name;
+		this.mt = mt;
+	}
+
+	@Override
+	public ControlFlow runForward(Memory m) {
+		
+		oldExists = m.isDefined( name, mt );
+		T oldVal = m.read( name, mt );
+		m.declare( name, value( m.createReadOnlyMemory() ), mt );
+		actions.push( (Memory mem) -> {
+			mem.undeclare( name, mt );
+            if( oldExists )
+				mem.write( name, oldVal, mt );
+		});
+		return new ControlFlow( ControlFlow.STEP_OVER );
+	}
+	
+	abstract protected T value( ReadOnlyMemory m );
+}

+ 72 - 0
src/codelines/ForEachLoop.java

@@ -0,0 +1,72 @@
+package codelines;
+
+import java.util.List;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.StackFrame.FrameType;
+
+public abstract class ForEachLoop <T> extends CodeLine {
+
+	String loopVar;
+	
+	public ForEachLoop( String varName )
+	{
+		this.loopVar = varName;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		boolean declared = false; // prove if it is the first step in the loop
+		if( !m.isSomewhereDefined( "line_" + lineId + "_index", MemoryType.LOCAL ) )
+		{ // first loop step
+			m.declare( "line_" + lineId + "_index", 0, MemoryType.LOCAL );
+			declared = true;
+		}
+		if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > list( m.createReadOnlyMemory() ).size() - 1 ) // prove if the loop has finished
+		{
+			m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			actions.push( (Memory mem) -> {});
+			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
+		}
+		if( declared )
+		{
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+            m.declare( loopVar, list( m.createReadOnlyMemory() ).get( m.read( "line_" + lineId + "_index", MemoryType.LOCAL ) ), MemoryType.LOCAL ); // set loop variable
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			} );
+		}
+		else
+		{
+			int oldIndex = m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL );
+			m.write( "line_" + lineId + "_index", oldIndex + 1, MemoryType.LOCAL ); // count index down
+			if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > list( m.createReadOnlyMemory() ).size() - 1 ) // prove if loop was finished
+			{
+				StackFrame sf = m.removeFrame(); // remove loop stack
+				m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+				actions.push( (Memory mem) -> {
+					mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+					mem.addFrame( sf ); // restore last loop stack
+				});
+				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+			}
+			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			m.declare( loopVar, list( m.createReadOnlyMemory() ).get( m.read( "line_" + lineId + "_index", MemoryType.LOCAL ) ), MemoryType.LOCAL ); // update loop variable
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.addFrame( old );
+				mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+			});
+		}
+		return new ControlFlow( ControlFlow.STEP_INTO );
+	}
+	
+	abstract protected List<T> list( ReadOnlyMemory m );
+}

+ 75 - 0
src/codelines/ForLoop.java

@@ -0,0 +1,75 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.StackFrame.FrameType;
+
+public abstract class ForLoop extends CodeLine {
+
+	String loopVar;
+	
+	public ForLoop( String varName )
+	{
+		this.loopVar = varName;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		boolean declared = false; // prove if it is the first step in the loop
+		if( !m.isSomewhereDefined( "line_" + lineId + "_index", MemoryType.LOCAL ) )
+		{ // first loop step
+			m.declare( "line_" + lineId + "_index", minimum( m.createReadOnlyMemory() ), MemoryType.LOCAL );
+			declared = true;
+		}
+		if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > maximum( m.createReadOnlyMemory() ) ) // prove if the loop has finished
+		{
+			m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			actions.push( (Memory mem) -> {});
+			return new ControlFlow( ControlFlow.STEP_OVER ); // don't execute the loop body
+		}
+		if( declared )
+		{
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+            m.declare( loopVar, minimum( m.createReadOnlyMemory() ), MemoryType.LOCAL ); // set loop variable
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+			} );
+		}
+		else
+		{
+			int oldIndex = m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL );
+			m.write( "line_" + lineId + "_index", oldIndex + 1, MemoryType.LOCAL ); // count index down
+			if( m.<Integer>read( "line_" + lineId + "_index", MemoryType.LOCAL ) > maximum( m.createReadOnlyMemory() ) ) // prove if loop was finished
+			{
+				StackFrame sf = m.removeFrame(); // remove loop stack
+				m.undeclare( "line_" + lineId + "_index", MemoryType.LOCAL );
+                //int old = m.read( loopVar, false );
+                //m.undeclare( loopVar, false );
+				actions.add( (Memory mem) -> {
+	                //m.declare( loopVar, old, false );
+					mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+					mem.addFrame( sf ); // restore last loop stack
+				});
+				return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+			}
+			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+			m.declare( loopVar, (int)m.read( "line_" + lineId + "_index", MemoryType.LOCAL ), MemoryType.LOCAL ); // update loop variable
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.addFrame( old );
+				mem.write( "line_" + lineId + "_index", oldIndex, MemoryType.LOCAL );
+			});
+		}
+		return new ControlFlow( ControlFlow.STEP_INTO );
+	}
+	
+	abstract protected int minimum( ReadOnlyMemory m );
+	
+	abstract protected int maximum( ReadOnlyMemory m );
+}

+ 39 - 0
src/codelines/FunctionCall.java

@@ -0,0 +1,39 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.PseudoCodeNode;
+import animation.Memory.MemoryType;
+
+public class FunctionCall extends CodeLine {
+
+	private PseudoCodeNode target;
+	private String[] params;
+	
+	public FunctionCall( PseudoCodeNode function, String[] params )
+	{
+		this.target = function;
+		this.params = params;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		int index = 1;
+		for( String p : params )
+		{
+			m.declare( "param" + index, m.read( p, MemoryType.LOCAL ), MemoryType.GLOBAL );
+			index++;
+		}
+		actions.push( (Memory mem) -> {
+			int ind = 1;
+			for( @SuppressWarnings("unused") String p : params )
+			{
+				m.undeclare( "param" + ind, MemoryType.GLOBAL );
+				ind++;
+			}
+		} );
+		return new ControlFlow( target );
+	}
+
+}

+ 54 - 0
src/codelines/FunctionDefinition.java

@@ -0,0 +1,54 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.StackFrame;
+import animation.StackFrame.FrameType;
+
+public class FunctionDefinition extends CodeLine {
+
+	private String[] params;
+	
+	public FunctionDefinition( String[] params )
+	{
+		this.params = params;
+	}
+	
+	@Override
+	public ControlFlow runForward(Memory m) {
+		if( !m.isDefined( "line_" + lineId + "_inside", MemoryType.LOCAL ) )
+		{
+			m.addFrame( new StackFrame( FrameType.FUNCTION ) );
+			m.declare( "line_" + lineId + "_inside", true, MemoryType.LOCAL );
+			int index = 1;
+			Object olds[] = new Object[ params.length ];
+			for( String p : params )
+			{
+				olds[ index - 1 ] = m.read( "param" + index, MemoryType.GLOBAL );
+				m.declare( p, olds[ index - 1 ], MemoryType.LOCAL );
+				m.undeclare( "param" + index, MemoryType.GLOBAL );
+				index++;
+			}
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				int i = 1;
+				for( @SuppressWarnings("unused") String p : params )
+				{
+					mem.declare( "param" + i, olds[ i - 1], MemoryType.GLOBAL );
+					i++;
+				}
+			} );
+			return new ControlFlow( ControlFlow.STEP_INTO );
+		}
+		else
+		{
+			StackFrame frame = m.removeFrame();
+			actions.push( (Memory mem) -> {
+				mem.addFrame( frame );
+			} );
+			return new ControlFlow( ControlFlow.STEP_OVER ); // return
+		}
+	}
+}

+ 45 - 0
src/codelines/IfLoop.java

@@ -0,0 +1,45 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.StackFrame.FrameType;
+
+/**
+ * Yey, IF is a LOOP.
+ * @author kolja
+ *
+ */
+public abstract class IfLoop extends CodeLine {
+
+	@Override
+	public ControlFlow runForward(Memory m) {
+		if( m.isDefined( "line_" + lineId + "_inside", MemoryType.LOCAL ) )
+		{
+			StackFrame old = m.removeFrame();
+			actions.push( (Memory mem) -> {
+				mem.addFrame( old );
+			} );
+            return new ControlFlow( ControlFlow.STEP_OVER );
+		}
+		else
+		{
+            if( condition( m.createReadOnlyMemory() ) ) {
+            	m.addFrame( new StackFrame( FrameType.LOOP ) );
+            	m.declare( "line_" + lineId + "_inside", true, MemoryType.LOCAL );
+            	actions.push( (Memory mem) -> {
+            		mem.removeFrame();
+            	} );
+            	return new ControlFlow( ControlFlow.STEP_INTO );
+            } else {
+            	actions.push( (Memory mem) -> {} );
+                return new ControlFlow( ControlFlow.STEP_OVER );
+            }
+		}
+	}
+
+	abstract protected boolean condition( ReadOnlyMemory m );
+}

+ 35 - 0
src/codelines/SetVariable.java

@@ -0,0 +1,35 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+
+public abstract class SetVariable <T> extends CodeLine {
+
+    private boolean oldExists;
+    private String name;
+    
+    public SetVariable( String name )
+    {
+        this.name = name;
+    }
+
+    @Override
+    public ControlFlow runForward(Memory m) {
+        
+        oldExists = m.isDefined( name, MemoryType.LOCAL );
+        T oldVal = m.read( name, MemoryType.LOCAL );
+        m.write( name, value( m.createReadOnlyMemory() ), MemoryType.LOCAL );
+        actions.push( (Memory mem) -> {
+            if( !oldExists )
+                mem.undeclare( name, MemoryType.LOCAL );
+            else
+                mem.write( name, oldVal, MemoryType.LOCAL ); // TODO write in correct stack frame
+        });
+        return new ControlFlow( ControlFlow.STEP_OVER );
+    }
+    
+    abstract protected T value( ReadOnlyMemory m );
+}

+ 57 - 0
src/codelines/WhileLoop.java

@@ -0,0 +1,57 @@
+package codelines;
+
+import animation.CodeLine;
+import animation.ControlFlow;
+import animation.Memory;
+import animation.StackFrame;
+import animation.Memory.MemoryType;
+import animation.Memory.ReadOnlyMemory;
+import animation.StackFrame.FrameType;
+
+public abstract class WhileLoop extends CodeLine {
+
+	@Override
+	public ControlFlow runForward(Memory m) {
+		boolean declared = false; // prove if it is the first step in the loop
+		if( !m.isDefined( "line_" + lineId + "_index", MemoryType.LOCAL ) )
+		{ // first loop step
+			declared = true;
+		}
+        if( !condition( m.createReadOnlyMemory() ) ) // prove if loop was finished
+        {
+            if( !declared )
+            {
+                StackFrame sf = m.removeFrame(); // remove loop stack
+                actions.push( (Memory mem) -> {
+                    mem.addFrame( sf ); // restore last loop stack
+                });
+            }
+            else
+            {
+                actions.push( (Memory mem) -> {});
+            }
+            return new ControlFlow( ControlFlow.STEP_OVER ); // step out of the loop
+        }
+		if( declared )
+		{
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+            m.declare( "line_" + lineId + "_index", 0, MemoryType.LOCAL );
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+			} );
+		}
+		else
+		{
+			StackFrame old = m.removeFrame(); // fresh stack frame for loop body
+			m.addFrame( new StackFrame( FrameType.LOOP ) );
+            m.declare( "line_" + lineId + "_index", 0, MemoryType.LOCAL );
+			actions.push( (Memory mem) -> {
+				mem.removeFrame();
+				mem.addFrame( old );
+			});
+		}
+		return new ControlFlow( ControlFlow.STEP_INTO );
+	}
+
+	abstract protected boolean condition( ReadOnlyMemory m );
+}

+ 1 - 1
src/graph/InitializeNodePositions.java

@@ -3,7 +3,7 @@ package graph;
 import java.awt.Color;
 import java.util.ArrayList;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 
 
 /**

+ 6 - 1
src/graph/LayeredEdge.java

@@ -5,7 +5,7 @@ import java.util.ArrayList;
 
 import org.eclipse.elk.graph.ElkEdge;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 import view.NodeView;
 
 /**
@@ -349,4 +349,9 @@ public class LayeredEdge implements LayeredGraphEdge {
     public boolean isReversedEdge() {
         return reversed;
     }
+    
+    @Override
+    public String toString() {
+        return "(" + sources.get( 0 ).toString() + "," + targets.get( 0 ).toString() + ")";
+    }
 }

+ 1 - 1
src/graph/LayeredGraphEdge.java

@@ -5,7 +5,7 @@ import java.util.ArrayList;
 
 import org.eclipse.elk.graph.ElkEdge;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 
 /**
  * Ein Interface, welches die Methoden einer Kante aus einem gelayerten Graphen beschreibt

+ 9 - 1
src/graph/LayeredGraphNode.java

@@ -6,7 +6,7 @@ import java.util.ArrayList;
 import org.eclipse.elk.graph.ElkEdge;
 import org.eclipse.elk.graph.ElkNode;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 
 /**
  * Ein Interface, welches die Methoden eines Knotens aus einem gelayerten
@@ -408,6 +408,14 @@ public interface LayeredGraphNode {
    */
   public ArrayList<LayeredGraphEdge> getSortedIncomingEdges(LayeredGraphNode n);
 
+  /**
+   * Ermittelt die Kante zwischen zwei Knoten
+   * @param source Der Source Knoten der Kante
+   * @param target Der Target Knoten der Kante
+   * @return noll, falls die Kante nicht existiert. Sonst die Kante von source nach target
+   */
+  public LayeredGraphEdge findEdgeBetween( LayeredGraphNode source, LayeredGraphNode target );
+  
   /**
    * F�gt einen neuen Knoten zum Subgraph hinzu
    * 

+ 63 - 32
src/graph/LayeredNode.java

@@ -8,7 +8,7 @@ import org.eclipse.elk.graph.ElkEdge;
 import org.eclipse.elk.graph.ElkNode;
 import org.eclipse.emf.common.util.EList;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 import view.NodeView;
 
 /**
@@ -61,7 +61,7 @@ public class LayeredNode implements LayeredGraphNode {
 
     /**
      * Konvertiert einen Graph aus dem Elk format in einen Graph, der mehr Informationen enth�lt
-     * 
+     *
      * @param n
      *            Der Graph, welcher konvertiert werden soll
      * @return Ein layered Graph, welcher im wesentlichen ein Wrapper f�r den urspr�nglichen Graphen ist
@@ -92,21 +92,28 @@ public class LayeredNode implements LayeredGraphNode {
         edges = new ArrayList<>();
         nodes = new ArrayList<>();
         layers = new ArrayList<>();
-        layouts = new LayoutInfo[4];
-        for (int i = 0; i < 4; i++)
-            layouts[i] = new LayoutInfo();
-        for (LayoutInfo l : layouts) {
-            if (original != null) {
-                l.x = original.getX();
-                l.y = original.getX();
-                l.w = original.getWidth();
-                l.h = original.getHeight();
+        layouts = new LayoutInfo[ 4 ];
+        for( int i = 0; i < 4; i++ )
+            layouts[ i ] = new LayoutInfo();
+        int index = 0;
+        for( LayoutInfo l : layouts )
+        {
+            if( original != null )
+            {
+            	l.x = original.getX();
+            	l.y = original.getX();
+            	l.w = original.getWidth();
+            	l.h = original.getHeight();
             }
             l.align = this;
             l.root = this;
             l.sink = this;
-            l.shift = Double.POSITIVE_INFINITY;
+            if( index == 0 || index == 2 )
+                l.shift = Double.POSITIVE_INFINITY;
+            else
+                l.shift = Double.NEGATIVE_INFINITY;
             l.xUndef = true;
+            index++;
         }
         dummy = false;
         combined = new CombinedLayoutInfo();
@@ -767,9 +774,10 @@ public class LayeredNode implements LayeredGraphNode {
     @Override
     public ArrayList<LayeredGraphEdge> getOutgoingEdges(LayeredGraphNode n) {
         ArrayList<LayeredGraphEdge> result = new ArrayList<>();
-        for (LayeredGraphEdge e : edges) {
-            if (e.getSources().contains(n))
-                result.add(e);
+        for( LayeredGraphEdge e : edges )
+        {
+            if( e.getSources().contains( n ) && !result.contains( e ) )
+                result.add( e );
         }
         return result;
     }
@@ -777,9 +785,10 @@ public class LayeredNode implements LayeredGraphNode {
     @Override
     public ArrayList<LayeredGraphEdge> getIncomingEdges(LayeredGraphNode n) {
         ArrayList<LayeredGraphEdge> result = new ArrayList<>();
-        for (LayeredGraphEdge e : edges) {
-            if (e.getTargets().contains(n))
-                result.add(e);
+        for( LayeredGraphEdge e : edges )
+        {
+            if( e.getTargets().contains( n ) && !result.contains( e ) )
+                result.add( e );
         }
         return result;
     }
@@ -787,13 +796,15 @@ public class LayeredNode implements LayeredGraphNode {
     @Override
     public ArrayList<LayeredGraphEdge> getSortedOutgoingEdges(LayeredGraphNode n) {
         ArrayList<LayeredGraphEdge> result = new ArrayList<>();
-        if (n.getLayer() + 1 >= layers.size())
-            return result;
-        ArrayList<LayeredGraphEdge> unsorted = getOutgoingEdges(n);
-        for (LayeredGraphNode node : layers.get(n.getLayer() + 1)) {
-            for (LayeredGraphEdge e : unsorted) {
-                if (e.getTargets().contains(node))
-                    result.add(e);
+    	if( n.getLayer() + 1 >= layers.size() )
+    		return result;
+        ArrayList< LayeredGraphEdge > unsorted = getOutgoingEdges( n );
+        for( LayeredGraphNode node : layers.get( n.getLayer() + 1 ) )
+        {
+        	for( LayeredGraphEdge e : unsorted )
+            {
+                if( e.getTargets().contains( node ) && !result.contains( e ) )
+                	result.add( e );
             }
         }
         return result;
@@ -802,18 +813,31 @@ public class LayeredNode implements LayeredGraphNode {
     @Override
     public ArrayList<LayeredGraphEdge> getSortedIncomingEdges(LayeredGraphNode n) {
         ArrayList<LayeredGraphEdge> result = new ArrayList<>();
-        if (n.getLayer() - 1 < 0)
-            return result;
-        ArrayList<LayeredGraphEdge> unsorted = getIncomingEdges(n);
-        for (LayeredGraphNode node : layers.get(n.getLayer() - 1)) {
-            for (LayeredGraphEdge e : unsorted) {
-                if (e.getSources().contains(node))
-                    result.add(e);
+    	if( n.getLayer() - 1 < 0 )
+    		return result;
+        ArrayList< LayeredGraphEdge > unsorted = getIncomingEdges( n );
+        for( LayeredGraphNode node : layers.get( n.getLayer() - 1 ) )
+        {
+        	for( LayeredGraphEdge e : unsorted )
+            {
+                if( e.getSources().contains( node ) && !result.contains( e ) )
+                	result.add( e );
             }
         }
         return result;
     }
 
+    @Override
+    public LayeredGraphEdge findEdgeBetween( LayeredGraphNode source, LayeredGraphNode target )
+    {
+        for( LayeredGraphEdge e : edges )
+        {
+            if( e.getSources().contains( source ) && e.getTargets().contains( target ) )
+                return e;
+        }
+        return null;
+    }
+
     @Override
     public LayeredGraphNode createNode(ElkNode original) {
         LayeredGraphNode n = new LayeredNode(original, this);
@@ -883,4 +907,11 @@ public class LayeredNode implements LayeredGraphNode {
     public void setParent(LayeredGraphNode parent) {
         this.parent = parent;
     }
+
+    @Override
+    public String toString() {
+        if( name != null )
+            return name;
+        return "unnamed node";
+    }
 }

+ 1 - 1
src/graph/RandomGraphGenerator.java

@@ -85,7 +85,7 @@ public class RandomGraphGenerator {
                 node = createRandomNode( parent, depth, false );
             }
             if( !validate( node ) )
-                throw new IllegalArgumentException( "No coherent graph found with this parameters.");
+                throw new IllegalArgumentException( "No acceptable graph found with this parameters.");
         }
         return node;
     }

+ 3 - 3
src/graph/io/Reader.java

@@ -99,11 +99,11 @@ public class Reader {
     private LayeredGraphEdge parseEdge( JSONObject edge, LayeredGraphNode parent ) throws JSONException
     {
         if( !edge.has( "source" ) || !edge.has( "target" ) )
-            throw new JSONException( edge + " is no valid Layered Graph Edge." );
+            throw new JSONException( edge + " is not a valid LayeredGraphEdge." );
         if( parent.findNodeByName( edge.getString( "source" ) ) == null )
-            throw new JSONException( edge + " is no valid Layered Graph Edge." );
+            throw new JSONException( edge + " is not a valid LayeredGraphEdge." );
         if( parent.findNodeByName( edge.getString( "target" ) ) == null )
-            throw new JSONException( edge + " is no valid Layered Graph Edge." );
+            throw new JSONException( edge + " is not a valid LayeredGraphEdge." );
         LayeredGraphEdge newEdge = parent.createSimpleEdge( null, parent.findNodeByName( edge.getString( "source" ) ), parent.findNodeByName( edge.getString( "target" ) ) );
         if( parent.findNodeByName( edge.getString( "source" ) ).isDummyNode() && parent.findNodeByName( edge.getString( "target" ) ).isDummyNode() )
             newEdge.setDummyEdge();

+ 2 - 2
src/lib/SweepCrossingMinimizer.java

@@ -20,7 +20,7 @@ public class SweepCrossingMinimizer {
      */
     public void minimizeCrossings( LayeredGraphNode graph ) {
         ArrayList< ArrayList< LayeredGraphNode > > layers = graph.getContainedLayers();
-        System.out.println( "Number of Detected Crossings before minimization: " + numberOfCrossings( layers ) );
+        System.out.println( "Number of detected crossings before minimization: " + numberOfCrossings( layers ) );
         for( int i = 1; i < layers.size(); i++ )
         { // Gehe alle Layer durch
             ArrayList<LayeredGraphNode> l1 = layers.get( i - 1 );
@@ -33,7 +33,7 @@ public class SweepCrossingMinimizer {
             ArrayList<LayeredGraphNode> l2 = layers.get( i );
             graph.setOrderedLayer( reduceCrossingsBetweenLayers( l1, l2, true ), i );  // minnimiere die Kreuzungen zwischen den letzten beiden Layern
         }
-        System.out.println( "Number of Detected Crossings after minimization: " + numberOfCrossings( layers ) );
+        System.out.println( "Number of detected crossings after minimization: " + numberOfCrossings( layers ) );
     }
     
     /**

+ 23 - 4
src/lib/TextLayoutHelper.java

@@ -1,5 +1,9 @@
 package lib;
 
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class TextLayoutHelper {
     /**
      * Modifies the given string such that its lenght matches the given length.
@@ -20,8 +24,8 @@ public class TextLayoutHelper {
         return s;
     }
     
-    private static String[] keywords = { "for", "do", "to", "then", "else", "if", "foreach", "while", "or", "and" };
-    private static String[] delimiter = { "\\+", "\\-", "\\[", "\\]", "\\|", " ", "^", "$", "\\=", "\\,", "\\(", "\\;" };
+    private static String[] keywords = { "for", "do", "to", "then", "else", "if", "foreach", "while", "or", "and", "call", "function", "false", "true", "undefined", "with" };
+    private static String[] delimiter = { "\\+", "\\-", "\\[", "\\]", "\\|", " ", "^", "$", "\\=", "\\,", "\\(", "\\;", "\\.", "\\)" };
     
     private static String getDelimiterRegex()
     {
@@ -34,16 +38,18 @@ public class TextLayoutHelper {
         return reg;
     }
     
-    public static String setupPseudoCodeStage( String s )
+    public static String setupPseudoCodeComment( String s )
     {
         return "<html><font color=#FDD017>"+s+"</font></html>";
     }
     
     public static String setupPseudoCode( String s, String[] vars )
     {
+        if( s.startsWith( "--" ) )
+            return setupPseudoCodeComment( s );
         String delimiter = getDelimiterRegex();
         String ret = "<html>";
-        String current = s.replaceAll( "&", "&amp" ).replaceAll( "<", "&lt").replaceAll( ">", "&gt");
+        String current = s.replaceAll( "&", "&amp" ).replaceAll( "<", " &lt ").replaceAll( ">", " &gt ");
         for( String k : keywords )
         {
             current = current.replaceAll( delimiter + "(" + k + ")" + delimiter, "$1<font color=orange>$2</font>$3" );
@@ -52,7 +58,20 @@ public class TextLayoutHelper {
         {
             current = current.replaceAll( delimiter + "(" + v + ")" + delimiter, "$1<font color=#3BB9FF>$2</font>$3" );
         }
+        current = current.replaceAll( "\\'(.*?)\\'", "'<font color=#3cb371>$1</font>'" );
         ret += current + "</html>";
         return ret;
     }
+
+    public static String[] getVariables( String s )
+    {
+        ArrayList<String> list = new ArrayList<>();
+        Matcher m = Pattern.compile( "<font color=#3BB9FF>(.*?)</font>" ).matcher( s );
+        while( m.find())
+        {
+            if( !list.contains( s.substring( m.start( 1 ), m.end( 1 ) ) ) )
+                list.add( s.substring( m.start( 1 ), m.end( 1 ) ) );
+        }
+        return list.toArray( new String[ list.size() ] );
+    }
 }

+ 2 - 2
src/main/Main.java

@@ -22,5 +22,5 @@ public class Main {
         InitializeNodePositions.placeNodes( graph );
         new MainView( graph );
     }
-
-}
+ 
+} 

+ 1 - 1
src/view/EdgeView.java

@@ -10,7 +10,7 @@ import java.util.ArrayList;
 
 import javax.swing.JPanel;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 import graph.LayeredGraphEdge;
 
 /**

+ 149 - 8
src/view/MainView.java

@@ -20,7 +20,10 @@ import javax.swing.JFileChooser;
 import javax.swing.JFrame;
 import javax.swing.JLabel;
 import javax.swing.JLayeredPane;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
 import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
 import javax.swing.JScrollPane;
 import javax.swing.JSplitPane;
 import javax.swing.JTextArea;
@@ -38,7 +41,7 @@ import animation.Action;
 import animation.AnimationController;
 import animation.PseudoCodeNode;
 import bk.BKNodePlacement;
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 import graph.InitializeNodePositions;
 import graph.LayeredGraphEdge;
 import graph.LayeredGraphNode;
@@ -423,6 +426,8 @@ public class MainView {
         pseudoTree.setModel( new DefaultTreeModel( tree ) );
         pseudoTree.setCellRenderer( new PseudoCodeRenderer() );
         pseudoTree.setSelectionModel( null );
+        pseudoTree.setToolTipText("");
+        pseudoTree.putClientProperty("JTree.lineStyle", "Angled");
         pseudoTree.addMouseListener( new MouseAdapter() {
             public void mousePressed(MouseEvent e) {
                 TreePath selPath = pseudoTree.getPathForLocation(e.getX(), e.getY());
@@ -441,10 +446,112 @@ public class MainView {
                     pseudoTree.repaint();
                     frame.repaint();
                 }
+                if( e.getButton() == MouseEvent.BUTTON3 )
+                {
+                    JPopupMenu menu = new JPopupMenu();
+                    JMenu expandM = new JMenu( "expand ..." );
+                    JMenuItem exAll = new JMenuItem( "all" );
+                    exAll.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = 0; i < pseudoTree.getRowCount(); i++ )
+                                pseudoTree.expandRow( i );
+                        }
+                    });
+                    expandM.add( exAll );
+                    JMenuItem exTop = new JMenuItem( "top level lines" );
+                    exTop.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = 0; i < pseudoTree.getRowCount(); i++ )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() < 3 )
+                                    pseudoTree.expandRow( i );
+                            }
+                        }
+                    });
+                    expandM.add( exTop );
+                    JMenuItem exSec = new JMenuItem( "second level lines" );
+                    exSec.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = 0; i < pseudoTree.getRowCount(); i++ )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() < 4 )
+                                    pseudoTree.expandRow( i );
+                            }
+                        }
+                    });
+                    expandM.add( exSec );
+                    JMenuItem exThi = new JMenuItem( "third level lines" );
+                    exThi.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = 0; i < pseudoTree.getRowCount(); i++ )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() < 5 )
+                                    pseudoTree.expandRow( i );
+                            }
+                        }
+                    });
+                    expandM.add( exThi );
+                    menu.add( expandM );
+                    JMenu collapsM = new JMenu( "collapse ..." );
+                    JMenuItem colAll = new JMenuItem( "all" );
+                    colAll.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = pseudoTree.getRowCount() - 1; i > 0; i-- )
+                                pseudoTree.collapseRow( i );
+                        }
+                    });
+                    collapsM.add( colAll );
+                    JMenuItem colTop = new JMenuItem( "top level lines" );
+                    colTop.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = pseudoTree.getRowCount() - 1; i > 0; i-- )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() >= 2 )
+                                    pseudoTree.collapseRow( i );
+                            }
+                        }
+                    });
+                    collapsM.add( colTop );
+                    JMenuItem colSec = new JMenuItem( "second level lines" );
+                    colSec.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = pseudoTree.getRowCount() - 1; i > 0; i-- )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() >= 3 )
+                                    pseudoTree.collapseRow( i );
+                            }
+                        }
+                    });
+                    collapsM.add( colSec );
+                    JMenuItem colThi = new JMenuItem( "third level lines" );
+                    colThi.addActionListener( new ActionListener() {
+                        @Override
+                        public void actionPerformed(ActionEvent e) {
+                            for( int i = pseudoTree.getRowCount() - 1; i > 0; i-- )
+                            {
+                                if( pseudoTree.getPathForRow( i ).getPathCount() >= 4 )
+                                    pseudoTree.collapseRow( i );
+                            }
+                        }
+                    });
+                    collapsM.add( colThi );
+                    menu.add( collapsM );
+                    menu.show( e.getComponent(), e.getX(), e.getY() );
+                }
             }
         } );
         pseudoTree.setRowHeight(15);
+        ((PseudoCodeRenderer)pseudoTree.getCellRenderer()).setMemory( algorithm.getProcessor().getMemory());
         JScrollPane treeView = new JScrollPane( pseudoTree );
+        PseudoCodeLines lineView = new PseudoCodeLines( pseudoTree );
+        treeView.setRowHeaderView( lineView );
         treeView.setBounds( 10,  110,  390, 380 );
         
         JTextArea debugText = new JTextArea();
@@ -455,7 +562,7 @@ public class MainView {
         JScrollPane debugView = new JScrollPane( debugText );
         debugView.setBounds( treeView.getX(), treeView.getY() + 500, treeView.getWidth(), 250 );
         
-        frame.setSize( (int)graph.getWidth( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 575, (int)graph.getHeight( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 200 );
+        frame.setSize( (int)graph.getWidth( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 450, (int)graph.getHeight( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 50 );
         frame.setLocation( 100, 100 );
         frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
         frame.setVisible( true );
@@ -468,15 +575,45 @@ public class MainView {
         pl.setLocation( 0, 0 );
         pl.setSize( frame.getSize() );
         NodeView topLeft = createNodeView( graph, LayoutType.TOP_BOTTOM_LEFT );
+        topLeft.addMouseMotionListener( new MouseAdapter() {
+            @Override
+            public void mouseMoved( MouseEvent e ) {
+                topLeft.setToolTipText( topLeft.updateTooltipText( e.getX(), e.getY() ) );
+            }
+        });
         NodeView topRight = createNodeView( graph, LayoutType.TOP_BOTTOM_RIGHT );
+        topRight.addMouseMotionListener( new MouseAdapter() {
+            @Override
+            public void mouseMoved( MouseEvent e ) {
+                topRight.setToolTipText( topRight.updateTooltipText( e.getX(), e.getY() ) );
+            }
+        });
         NodeView bottomLeft = createNodeView( graph, LayoutType.BOTTOM_TOP_LEFT );
+        bottomLeft.addMouseMotionListener( new MouseAdapter() {
+            @Override
+            public void mouseMoved( MouseEvent e ) {
+                bottomLeft.setToolTipText( bottomLeft.updateTooltipText( e.getX(), e.getY() ) );
+            }
+        });
         NodeView bottomRight = createNodeView( graph, LayoutType.BOTTOM_TOP_RIGHT );
+        bottomRight.addMouseMotionListener( new MouseAdapter() {
+            @Override
+            public void mouseMoved( MouseEvent e ) {
+                bottomRight.setToolTipText( bottomRight.updateTooltipText( e.getX(), e.getY() ) );
+            }
+        });
         pl.add( topLeft );
         pl.add( topRight );
         pl.add( bottomLeft );
         pl.add( bottomRight );
         layne.add( pl, 1 );
         NodeView combined = createNodeView( graph, LayoutType.COMBINED );
+        combined.addMouseMotionListener( new MouseAdapter() {
+            @Override
+            public void mouseMoved( MouseEvent e ) {
+                combined.setToolTipText( combined.updateTooltipText( e.getX(), e.getY() ) );
+            }
+        });
         combined.setSize( 500, 500 );
         layne.add( combined, 0 );
         
@@ -484,8 +621,6 @@ public class MainView {
         spane.setLeftComponent( layne );
         spane.setResizeWeight(0.5);
         
-        //JPanel onlyCurrentLayout = new JPanel();
-        //onlyCurrentLayout.setLayout( new BorderLayout() );
         JPanel menue = new JPanel();
         menue.setLayout( null );
         menue.setPreferredSize( new Dimension( 410, 500 ) );
@@ -515,7 +650,7 @@ public class MainView {
         menue.add( spane2 );
         
         spane.setRightComponent( menue);
-        spane.setContinuousLayout(true);
+        spane.setContinuousLayout( true );
         spane.setDividerLocation( frame.getWidth() - 430 );
         spane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() {
 
@@ -526,9 +661,7 @@ public class MainView {
             
         });
         frame.add( spane );
-        frame.setSize( frame.getWidth() + 1, frame.getHeight() );
-        frame.setSize( frame.getWidth() - 1, frame.getHeight() );
-        frame.validate();
+        frame.revalidate();
         frame.repaint();
 
         frame.addComponentListener(new ComponentAdapter()
@@ -586,6 +719,14 @@ public class MainView {
                 frame.repaint();
             }
         });
+        frame.setSize( frame.getWidth() + 1, frame.getHeight() );
+        frame.setSize( frame.getWidth() - 1, frame.getHeight() );
+        if( frame.getHeight() < (int)graph.getHeight( LayoutType.TOP_BOTTOM_LEFT ) * 2 + 50 )
+        {
+            double factor = (graph.getHeight( LayoutType.TOP_BOTTOM_LEFT ) * 2) / (frame.getHeight()-50);
+            frame.setSize( (int)((frame.getWidth() - 450) / factor) + 450, frame.getHeight() );
+            spane.setDividerLocation( frame.getWidth() - 430 );
+        }
 
         optionsDialog = new OptionsDialog();
         optionsDialog.addActionListener( new ActionListener() {

+ 40 - 1
src/view/NodeView.java

@@ -12,7 +12,7 @@ import javax.swing.BorderFactory;
 import javax.swing.JPanel;
 import javax.swing.border.Border;
 
-import bk.ExtremalLayoutCalc.LayoutType;
+import bk.LayoutType;
 import graph.LayeredGraphNode;
 
 /**
@@ -65,6 +65,45 @@ public class NodeView extends JPanel {
         return y;
     }
     
+    public String updateTooltipText( int mx, int my ) {
+        int x = 0;
+        int y = 0;
+        double scaleW = Math.min( (double)super.getWidth() / (int)model.getWidth( layout ), (double)super.getHeight() / (int)model.getHeight( layout ));
+        double scaleH = scaleW;
+        int width = (int)(super.getWidth() / scaleW);
+        if( scaleW == (double)super.getWidth() / (int)model.getWidth( layout ) )
+            y += (super.getHeight() - (model.getHeight( layout ) * scaleH )) / scaleH / 2;
+        if( scaleH == (double)super.getHeight() / (int)model.getHeight( layout ) )
+            x += (super.getWidth() - (model.getWidth( layout ) * scaleW )) / scaleW / 2;
+        if( model.isDummyNode() )
+        {
+            scaleW *= 1 / 4.0;
+            x += width / (3/4.0);
+        }
+        double minX = Double.POSITIVE_INFINITY;
+        for( Component c : getComponents() )
+        {
+            minX = Math.min( c.getLocation().x, minX);
+        }
+        for( Component c : getComponents() )
+        {
+            int nx = (int)(mx / scaleW) - (c.getLocation().x + 25 - (int)minX + x);
+            int ny = (int)(my / scaleH) - (c.getLocation().y + 25 + y);
+            int width1 = Math.min( (int)model.getWidth( layout ) - 25, c.getPreferredSize().width + 25 );
+            int height1 = Math.min( (int)model.getHeight( layout ) - 25, c.getPreferredSize().height + 25 );
+            if( nx < width1 && ny < height1 && nx > 0 && ny > 0 && c instanceof NodeView )
+                return ((NodeView)c).updateTooltipText( nx, ny );
+        }
+        if( layout != LayoutType.COMBINED )
+        {
+            return "<html>Name: " + model.toString() + 
+                    "<br>Root: " + model.getRoot( layout ).toString() +
+                    "<br>Shink: " + model.getSink( layout ).toString() + 
+                    "<br>Shift: " + model.getShift( layout ) + "</html>";
+        }
+        return "Name: " + model.toString(); 
+    }
+    
     @Override
     public void paint( Graphics g )
     {

+ 183 - 0
src/view/PseudoCodeLines.java

@@ -0,0 +1,183 @@
+package view;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
+import java.awt.RenderingHints;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.ImageIcon;
+import javax.swing.JComponent;
+import javax.swing.JScrollPane;
+import javax.swing.JTree;
+import javax.swing.JViewport;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import animation.PseudoCodeNode;
+
+/**
+ * JComponent used to draw line numbers. This JComponent should be added as a row header view in a JScrollPane. Based upon the 
+ * LineNumberModel provided, this component will draw the line numbers as needed.
+ * @author Greg Cope
+ *
+ */
+
+public class PseudoCodeLines extends JComponent implements MouseListener{
+   
+    static final long serialVersionUID = 432143214L;
+   
+    //pixel padding on left and right
+    private static final int HORIZONTAL_PADDING = 1;
+    //pixel padding on left and right
+    private static final int VERTICAL_PADDING = 3;
+    
+    private static ImageIcon currentLine = new ImageIcon( PseudoCodeNode.class.getResource( "/img/current_line.png" ) );
+    
+    private JTree tree;
+    
+    /**
+     * Constructs a new Component with no model
+     */
+    public PseudoCodeLines( JTree t ){
+        super();
+        tree = t;
+        this.addMouseListener( this );
+    }
+    
+    /**
+     * Checks and adjusts the width of this component based upon the line numbers
+     */
+    public void adjustWidth(){
+        int max = getLineNumber( (TreeNode)tree.getPathForRow( tree.getRowCount() - 1 ).getLastPathComponent() );
+        if ( getGraphics() == null ){
+            return;
+        }
+        int width = getGraphics().getFontMetrics().stringWidth(String.valueOf(max)) + 2 * HORIZONTAL_PADDING + 30;
+        JComponent c = (JComponent)getParent();
+        if (c == null){//not within a container
+            return;
+        }
+        Dimension dimension = c.getPreferredSize();
+        if ( c instanceof JViewport ){//do some climbing up the component tree to get the main JScrollPane view
+            JViewport view = (JViewport)c;
+            Component parent = view.getParent();
+            if ( parent != null && parent instanceof JScrollPane){
+                JScrollPane scroller = (JScrollPane)view.getParent();
+                dimension = scroller.getViewport().getView().getPreferredSize();
+            }           
+        }
+        if ( width > super.getPreferredSize().width || width < super.getPreferredSize().width){
+            setPreferredSize(new Dimension(width + 2*HORIZONTAL_PADDING, dimension.height));
+            revalidate();
+            repaint();
+        }
+    }
+    
+    @Override
+    public Dimension getPreferredSize()
+    {
+        return super.getPreferredSize();
+    }
+    
+    private int countChildren( TreeNode treeNode )
+    {
+        if( treeNode.isLeaf() )
+            return 0;
+        else
+        {
+            int sum = 0;
+            for( int i = 0; i < treeNode.getChildCount(); i++ )
+            {
+                sum += countChildren( treeNode.getChildAt( i ) ) + 1;
+            }
+            return sum;
+        }
+    }
+
+    private int getLineNumber( TreeNode treeNode )
+    {
+        TreeNode parent = treeNode.getParent();
+        if( parent == null )
+            return 1;
+        int sum = getLineNumber( parent ) + 1;
+        for( int i = 0; i < parent.getChildCount(); i++ )
+        {
+            if( i == parent.getIndex( treeNode ) )
+                return sum;
+            sum += countChildren( parent.getChildAt( i ) ) + 1;
+        }
+        return 0;
+    }
+    
+    @Override
+    public void paintComponent(Graphics g){
+        adjustWidth();
+        super.paintComponent(g);
+        Graphics2D g2d = (Graphics2D)g;
+        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        g.setColor(RenderHelper.BACKGROUND_COLOR);
+        g2d.fillRect(0, 0, getWidth(), getHeight());
+        g.setColor(RenderHelper.FOREGROUND_COLOR);
+        for ( int i = 0; i < tree.getRowCount(); i++ ){
+            PseudoCodeNode node = (PseudoCodeNode)tree.getPathForRow( i ).getLastPathComponent();
+            int number = getLineNumber( node );
+            Rectangle rect = tree.getRowBounds( i );
+            String text = String.valueOf( number );
+            int yPosition = rect.y + rect.height - VERTICAL_PADDING;
+            if( !node.hasSelectedSubnode() && node.isSelected() )
+                g.drawImage( currentLine.getImage(), HORIZONTAL_PADDING, rect.y + VERTICAL_PADDING - 5, 20, 20, null );
+            g2d.drawString( text, HORIZONTAL_PADDING * 2 + 20, yPosition );
+            if( node.hasBreakPoint() )
+            {
+                Color c = g.getColor();
+                g.setColor( new Color (0xe7887a) );
+                g.fillOval( getWidth() - HORIZONTAL_PADDING - 10, rect.y + VERTICAL_PADDING, 10, 10 );
+                g.setColor( c );
+            }
+        }
+    }
+    
+    private int getRowNumber( int y ) {
+        int curr = 0;
+        int res = -1;
+        for ( int i = 0; i < tree.getRowCount(); i++ ){
+            Rectangle rect = tree.getRowBounds( i );
+            if( y > curr && y < rect.y + rect.height )
+            {
+                res = i;
+                break;
+            }
+            curr = rect.y + rect.height;
+        }
+        return res;
+    }
+
+    @Override
+    public void mouseClicked(MouseEvent e) {
+        int rNum = getRowNumber( e.getY() );
+        if( rNum < 0 )
+            return;
+        TreePath path = tree.getPathForRow( rNum );
+        if( path == null )
+            return;
+        ((PseudoCodeNode)path.getLastPathComponent()).setBreakPoint( !((PseudoCodeNode)path.getLastPathComponent()).hasBreakPoint() );
+        tree.repaint();
+    }
+
+    @Override
+    public void mousePressed(MouseEvent e) {}
+
+    @Override
+    public void mouseReleased(MouseEvent e) {}
+
+    @Override
+    public void mouseEntered(MouseEvent e) {}
+
+    @Override
+    public void mouseExited(MouseEvent e) {}
+}

+ 40 - 66
src/view/PseudoCodeRenderer.java

@@ -2,17 +2,15 @@ package view;
 
 import java.awt.Color;
 import java.awt.Component;
-import java.awt.Dimension;
 import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.image.BufferedImage;
 
-import javax.swing.ImageIcon;
 import javax.swing.JTree;
 import javax.swing.tree.DefaultTreeCellRenderer;
-import javax.swing.tree.TreeNode;
 
+import animation.Memory;
+import animation.Memory.MemoryType;
 import animation.PseudoCodeNode;
+import lib.TextLayoutHelper;
 
 /**
  * A tree-like display of pseudocode.
@@ -23,9 +21,16 @@ import animation.PseudoCodeNode;
 public class PseudoCodeRenderer extends DefaultTreeCellRenderer {
 	
     private static final long serialVersionUID = 1L;
-    private static ImageIcon currentLine = new ImageIcon( PseudoCodeNode.class.getResource( "/img/current_line.png" ) );
     
     private Color specialColor = null;
+    
+    private Memory mem;
+    private String toolTip = "";
+    
+    public void setMemory( Memory m )
+    {
+        mem = m;
+    }
 
     @Override
     public Color getBackgroundNonSelectionColor() {
@@ -41,75 +46,24 @@ public class PseudoCodeRenderer extends DefaultTreeCellRenderer {
         return new Color(0xa9b7c6);
     }
     
-    private int countChildren( TreeNode treeNode )
-    {
-        if( treeNode.isLeaf() )
-            return 0;
-        else
-        {
-            int sum = 0;
-            for( int i = 0; i < treeNode.getChildCount(); i++ )
-            {
-                sum += countChildren( treeNode.getChildAt( i ) ) + 1;
-            }
-            return sum;
-        }
-    }
-
-    private int getLineNumber( TreeNode treeNode )
-    {
-        TreeNode parent = treeNode.getParent();
-        if( parent == null )
-            return 1;
-        int sum = getLineNumber( parent ) + 1;
-        for( int i = 0; i < parent.getChildCount(); i++ )
-        {
-            if( i == parent.getIndex( treeNode ) )
-                return sum;
-            sum += countChildren( parent.getChildAt( i ) ) + 1;
-        }
-        return 0;
-    }
-    
     @Override
     public Font getFont() {
         return new Font("Monospaced", Font.PLAIN, 12);
     }
     
+    @Override
+    public String getToolTipText()
+    {
+        return toolTip;
+    }
+    
     @Override
     public Component getTreeCellRendererComponent(JTree tree, Object value, boolean arg2, boolean arg3, boolean arg4, int arg5, boolean arg6) {
-
         PseudoCodeNode node = (PseudoCodeNode) value;
-        int width = 10;
-        if( !node.hasSelectedSubnode() && node.isSelected() )
-            width += 25;
-        if( node.hasBreakPoint() )
-            width += 15;
-        String line = "" + getLineNumber( (TreeNode) value );
-        width += tree.getFontMetrics( this.getFont() ).stringWidth( line );
-        this.setPreferredSize( new Dimension( width + tree.getFontMetrics( this.getFont() ).stringWidth( (String)node.getUserObject() ) + 5, 30 ) );
-        this.setSize( new Dimension( width + tree.getFontMetrics( this.getFont() ).stringWidth( (String)node.getUserObject() ) + 5, 30 ) );
         this.doLayout();
-        BufferedImage rowNumerImg = new BufferedImage( width, 30, BufferedImage.TYPE_INT_ARGB );
-        Graphics2D g = (Graphics2D) rowNumerImg.getGraphics();
-        int x = 5;
-        if( !node.hasSelectedSubnode() && node.isSelected() )
-        {
-        	g.drawImage( currentLine.getImage(), x, 5, 20, 20, null );
-        	x += 25;
-        }
-        g.setColor( RenderHelper.CURRENT_LINE_COLOR );
-        g.drawString( line, x, 20 );
-        x += g.getFontMetrics().stringWidth( line ) + 5;
-        if( node.hasBreakPoint() )
-        {
-            g.setColor( new Color (0xe7887a) );
-            g.fillOval(x, 10, 10, 10 );
-        }
-        g.dispose();
-        this.setClosedIcon( new ImageIcon( rowNumerImg ) );
-        this.setOpenIcon( new ImageIcon( rowNumerImg ) );
-        this.setLeafIcon( new ImageIcon( rowNumerImg ) );
+        this.setClosedIcon( null );
+        this.setOpenIcon( null );
+        this.setLeafIcon( null );
         super.getTreeCellRendererComponent(tree, value, arg2, arg3, arg4, arg5, arg6);
         specialColor = null;
         if(node.isSelected()) {
@@ -118,6 +72,26 @@ public class PseudoCodeRenderer extends DefaultTreeCellRenderer {
             specialColor = RenderHelper.BREAKPOINT_COLOR;
         }
         setText((String)node.getUserObject());
+        toolTip = "<html>";
+        for( String var : TextLayoutHelper.getVariables( (String)node.getUserObject() ) )
+        {
+            if( mem != null )
+            {
+                synchronized( mem ) {
+                    if( mem.isSomewhereDefined( var, MemoryType.LOCAL ) )
+                    {
+                        Object val = mem.read( var, MemoryType.LOCAL );
+                        if( val != null )
+                            toolTip += var + "=" + val.toString() + "<br>";
+                    }
+                }
+            }
+        }
+        if( toolTip.equals( "<html>" ) )
+            toolTip += "no variables found in the stack";
+        else
+            toolTip = toolTip.substring( 0, toolTip.length() - 4 );
+        toolTip += "</html>";
         return this;
     }