package com.horstmann.violet.workspace.editorpart.behavior;
import java.util.ArrayList;
import java.util.List;
import javax.swing.undo.AbstractUndoableEdit;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;
import javax.swing.undo.CompoundEdit;
import javax.swing.undo.UndoableEdit;
import com.horstmann.violet.product.diagram.abstracts.IGraph;
import com.horstmann.violet.product.diagram.abstracts.edge.IEdge;
import com.horstmann.violet.product.diagram.abstracts.node.INode;
import com.horstmann.violet.workspace.editorpart.IEditorPart;
/**
* Undo/Redo behavior triggered when node and edges are removed
*
* @author Alexandre de Pellegrin
*
*/
public class UndoRedoOnRemoveBehavior extends AbstractEditorPartBehavior
{
/**
* The concerned workspace
*/
private IEditorPart editorPart;
/**
* The global undo/redo behavior which contains all individual undo/redo behaviors
*/
private UndoRedoCompoundBehavior compoundBehavior;
/**
* Keeps all the node attached to the graph before the remove action
*/
private List<INode> nodesOnGraphBeforeRemove = new ArrayList<INode>();
/**
* Keeps all the edges attached to the graph before the remove action
*/
private List<IEdge> edgesOnGraphBeforeRemove = new ArrayList<IEdge>();
/**
* Default constructor
*
* @param editorPart
* @param compoundBehavior
*/
public UndoRedoOnRemoveBehavior(IEditorPart editorPart, UndoRedoCompoundBehavior compoundBehavior)
{
this.editorPart = editorPart;
this.compoundBehavior = compoundBehavior;
}
@Override
public void beforeRemovingSelectedElements()
{
this.nodesOnGraphBeforeRemove.clear();
this.edgesOnGraphBeforeRemove.clear();
this.nodesOnGraphBeforeRemove.addAll(this.editorPart.getGraph().getAllNodes());
this.edgesOnGraphBeforeRemove.addAll(this.editorPart.getGraph().getAllEdges());
}
@Override
public void afterRemovingSelectedElements()
{
List<INode> nodesOnGraphAfterAction = new ArrayList<INode>(this.editorPart.getGraph().getAllNodes());
List<IEdge> edgesOnGraphAfterAction = new ArrayList<IEdge>(this.editorPart.getGraph().getAllEdges());
List<INode> nodesReallyRemoved = new ArrayList<INode>();
nodesReallyRemoved.addAll(this.nodesOnGraphBeforeRemove);
nodesReallyRemoved.removeAll(nodesOnGraphAfterAction);
List<IEdge> edgesReallyRemoved = new ArrayList<IEdge>();
edgesReallyRemoved.addAll(this.edgesOnGraphBeforeRemove);
edgesReallyRemoved.removeAll(edgesOnGraphAfterAction);
this.compoundBehavior.startHistoryCapture();
CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit();
for (final IEdge aSelectedEdge : edgesReallyRemoved)
{
UndoableEdit edit = new AbstractUndoableEdit()
{
@Override
public void undo() throws CannotUndoException
{
IGraph graph = editorPart.getGraph();
graph.connect(aSelectedEdge, aSelectedEdge.getStartNode(), aSelectedEdge.getStartLocation(),
aSelectedEdge.getEndNode(), aSelectedEdge.getEndLocation(), aSelectedEdge.getTransitionPoints());
super.undo();
}
@Override
public void redo() throws CannotRedoException
{
super.redo();
IGraph graph = editorPart.getGraph();
graph.removeEdge(aSelectedEdge);
}
};
capturedEdit.addEdit(edit);
}
List<INode> filteredNodes = removeChildren(nodesReallyRemoved);
for (final INode aSelectedNode : filteredNodes)
{
UndoableEdit edit = new AbstractUndoableEdit()
{
@Override
public void undo() throws CannotUndoException
{
IGraph graph = editorPart.getGraph();
graph.addNode(aSelectedNode, aSelectedNode.getLocationOnGraph());
super.undo();
}
@Override
public void redo() throws CannotRedoException
{
super.redo();
IGraph graph = editorPart.getGraph();
graph.removeNode(aSelectedNode);
}
};
capturedEdit.addEdit(edit);
}
this.compoundBehavior.stopHistoryCapture();
this.nodesOnGraphBeforeRemove.clear();
this.edgesOnGraphBeforeRemove.clear();
}
/**
* Checks if ancestorNode is a parent node_old of child node_old
*
* @param childNode
* @param ancestorNode
* @return b
*/
private boolean isAncestorRelationship(INode childNode, INode ancestorNode)
{
INode parent = childNode.getParent();
if (parent == null)
{
return false;
}
List<INode> fifo = new ArrayList<INode>();
fifo.add(parent);
while (!fifo.isEmpty())
{
INode aParentNode = fifo.get(0);
fifo.remove(0);
if (aParentNode.equals(ancestorNode))
{
return true;
}
INode aGranParent = aParentNode.getParent();
if (aGranParent != null)
{
fifo.add(aGranParent);
}
}
return false;
}
/**
* Takes a list of node and removes from this list all node which have ancestors node_old in this list.<br/>
*
* @param nodes the list to filter
* @return the filtered list
*/
private List<INode> removeChildren(List<INode> nodes)
{
List<INode> result = new ArrayList<INode>();
for (INode aNode : nodes)
{
boolean isOrphelin = true;
for (INode aParent : nodes)
{
boolean isAncestorRelationship = isAncestorRelationship(aNode, aParent);
if (isAncestorRelationship)
{
isOrphelin = false;
}
}
if (isOrphelin)
{
result.add(aNode);
}
}
return result;
}
}