/*
* @(#)RotationDoubleBSTAnimation.java
*
* Last Modified: 9/15/01
*/
import java.util.*;
import java.awt.*;
import java.awt.geom.*;
/** *
* The Animation object that defines a double rotation of a node in a BSTTree. The animation builds two RotationBSTAnimations
* as it goes, keeping only one currently animating and allowing rewinding only to the previous
* rotation.
*
* The Object implements the Animation interface, and should consequently be used only in that
* context. The object restores all values changed in the given nodes, however, if the object
* is never allowed to finish, the restoring of values becomes impossible. On any exception occuring
* elsewhere, the object may not restore the conditions correctly.
*
* @author Corey Sanders
* @version 1.2 9/15/01
*/
public class RotationDoubleBSTAnimation extends AbstractAnimation implements AnimationListener {
/**
* Constant that defines the starting location.
*/
private final int FIRST_ROTATE = 0;
/**
* Constant that defines the fading of the erasing node.
*/
private final int SECOND_ROTATE = 1;
/**
* Private int used to hold the current and previous location steps.
*/
private int currentLocation = FIRST_ROTATE;
/**
* Refers to the first Rotation being drawn.
*/
RotationBSTAnimation firstRotation;
/**
* Refers to the second Rotation being drawn.
*/
RotationBSTAnimation secondRotation;
/**
* Holds the node that is replacing.
*/
private BSTTree node;
/**
* Private doubles used to hold the rotation location.
*/
private double rotationLocation = 0.0;
/**
* The constructor which initiates the status and prepares the color schemes. The node
* which is being deleted must be passed.
*
* @param node the BSTTree from which the partition takes place.
* @param startingCmd the Animation command that this should start.
* @param stepTime the time for each step of the Animation. Sets the initial value.
*/
public RotationDoubleBSTAnimation(BSTTree node, String startingCmd, int stepTime) {
super();
// Sets node
setNode(node);
// Sets starting command and time
setStartingCommand(startingCmd);
setStepTime(stepTime);
}
/**
* The constructor which initiates the status and sets the color schemes to null. No colors
* will be change using this animation. The node
* which is animating must be passed.
*
* @param node the BSTTree which is deleted during the deletion.
*/
public RotationDoubleBSTAnimation(BSTTree node) {
this(node, Animation.PLAY, DEFAULT_STEP);
}
/************************/
/* Accessor methods */
/************************/
/**
* Gets the node from which the partitioning takes place.
*
* @return BSTTree of the node currently being partitioned at the KeySelect.
*/
public BSTTree getNode() {
return node;
}
/************************/
/* Mutator methods */
/************************/
/**
* Sets the node from which the partitioning takes place.
*
* @param node BSTTree of the node currently being partitioned at the KeySelect.
*/
public void setNode(BSTTree node) {
this.node = node;
}
/*********************/
/* Animation methods */
/*********************/
/**
* Draws the animation of the next step, using the status of the animation (Animation.PLAY, Animation.PAUSE and so forth).
* After completing the drawing, the Animation sends an AnimationEvent to all its listeners, indicating
* any information that the listerners may wish to use.
*
* Other Animation Objects used:
*
*
* @param g2 the graphics to which the animation step should be drawn.
* @param startingStatus the status used as the starting command of animation, if needed.
*/
public void drawAnimation(Graphics2D g2, String startingStatus) {
setStartingCommand(startingStatus);
// Starting Finish status (set prior to call)
if (getStatus().equals(Animation.FINISH)) {
if (currentLocation == FIRST_ROTATE && firstRotation != null) {
firstRotation.setStatus(Animation.FINISH);
firstRotation.drawAnimation(g2, getStatus());
// Do actual rotation in tree (Setting the correct final location).
getNode().getHead().rotateUpTreeType(getNode());
}
else if (currentLocation == SECOND_ROTATE && secondRotation != null) {
secondRotation.setStatus(Animation.FINISH);
secondRotation.drawAnimation(g2, getStatus());
}
}
// BEGIN status
if (getStatus().equals(Animation.BEGIN)) {
animationAction();
messageAction(Animation.BEGIN + " Double Rotation of "+getNode().getKey().toString());
// set starting status
setStatus(getStartingCommand());
// Parent
BSTTree parentTree = (BSTTree)getNode().getParentTree();
// GrandParent
BSTTree grandParentTree = (BSTTree)parentTree.getParentTree();
// Top of tree, only single rotation possible
if (grandParentTree == getNode().getHead()) {
messageAction("Top of Tree : No Double Rotation");
secondRotation = (RotationBSTAnimation)((BSTTreeHead)getNode().getHead()).makeRotationAnimation(getNode());
secondRotation.addAnimationListener(this);
currentLocation = SECOND_ROTATE;
secondRotation.drawAnimation(g2, getStatus());
}
else {
// Opposite direction, resulting in normal rotations (left-right and right-left)
if (
((grandParentTree.getLeftTree() == parentTree) && (parentTree.getRightTree() == node))
||
((grandParentTree.getRightTree() == parentTree) && (parentTree.getLeftTree() == node))
) {
messageAction("Opposite Direction - Rotate Node Up Twice");
messageAction("*-----Start Rotation at"+node.getKey().toString()+"-----*");
// Rotate node up twice
firstRotation = (RotationBSTAnimation)((BSTTreeHead)getNode().getHead()).makeRotationAnimation(getNode());
}
else { // Same direction, Splay rotation
messageAction("Same Direction - Splay Rotate Parent First");
messageAction("*-----Start Rotation at"+parentTree.getKey().toString()+"-----*");
firstRotation = (RotationBSTAnimation)((BSTTreeHead)parentTree.getHead()).makeRotationAnimation(parentTree);
}
currentLocation = FIRST_ROTATE;
firstRotation.addAnimationListener(this);
firstRotation.drawAnimation(g2, getStatus());
}
// REDRAW
messageAction(Animation.REDRAW);
return;
}
// Currently on a step and no changes have occured. Return to startingStatus
if (getStatus().equals(Animation.STEP)) {
setStatus(getStartingCommand());
}
// PLAY status
if (getStatus().equals(Animation.PLAY)) {
messageAction(Animation.PLAY);
if (currentLocation == FIRST_ROTATE) {
// Set values for animation
firstRotation.setStepTime(getStepTime());
firstRotation.setStep(getStep());
// Set play status
firstRotation.setStatus(Animation.PLAY);
// Draw rotation animation
firstRotation.drawAnimation(g2, Animation.PLAY);
// Set up second rotation
if (firstRotation.getStatus().equals(Animation.FINISH)) {
messageAction("Second Rotation with "+getNode().getKey().toString());
secondRotation = (RotationBSTAnimation)((BSTTreeHead)getNode().getHead()).makeRotationAnimation(getNode());
messageAction(Animation.REDRAW);
secondRotation.addAnimationListener(this);
currentLocation = SECOND_ROTATE;
secondRotation.drawAnimation(g2, getStatus());
setStatus(Animation.STEP);
}
}
else if (currentLocation == SECOND_ROTATE) {
// Set values for animation
secondRotation.setStepTime(getStepTime());
secondRotation.setStep(getStep());
// Set play status
secondRotation.setStatus(Animation.PLAY);
// Draw rotation animation
secondRotation.drawAnimation(g2, Animation.PLAY);
if (secondRotation.getStatus().equals(Animation.FINISH)) {
setStatus(Animation.FINISH);
}
}
}
// REWIND status
if (getStatus().equals(Animation.REWIND)) {
messageAction(Animation.REWIND);
if (currentLocation == FIRST_ROTATE) {
// Set values for animation
firstRotation.setStepTime(getStepTime());
firstRotation.setStep(getStep());
// Set rewind status
firstRotation.setStatus(Animation.REWIND);
// Draw rotation animation
firstRotation.drawAnimation(g2, Animation.REWIND);
if (firstRotation.getStatus().equals(Animation.PAUSE)) {
setStatus(Animation.PAUSE);
}
}
if (currentLocation == SECOND_ROTATE) {
// Set values for animation
secondRotation.setStepTime(getStepTime());
secondRotation.setStep(getStep());
// Set rewind status
secondRotation.setStatus(Animation.REWIND);
// Draw rotation animation
secondRotation.drawAnimation(g2, Animation.REWIND);
if (secondRotation.getStatus().equals(Animation.PAUSE)) {
setStatus(Animation.PAUSE);
}
}
}
// PAUSE status
if (getStatus().equals(Animation.PAUSE)) {
messageAction(Animation.PAUSE);
if (currentLocation == FIRST_ROTATE) {
// Set pause status
firstRotation.setStatus(Animation.PAUSE);
// Draw rotation animation
firstRotation.drawAnimation(g2, Animation.PAUSE);
}
if (currentLocation == SECOND_ROTATE) {
// Set pause status
secondRotation.setStatus(Animation.PAUSE);
// Draw rotation animation
secondRotation.drawAnimation(g2, Animation.PAUSE);
}
}
// STOP status
if (getStatus().equals(Animation.STOP)) {
messageAction(Animation.STOP);
return;
}
// FINISH status
if (getStatus().equals(Animation.FINISH)) {
messageAction(Animation.FINISH);
messageAction("*--------Double Rotation of "+getNode().getKey().toString()+"--------*");
}
// Call listeners
animationAction();
}
/**
* Calls all of the listeners of the current Animation and passed information regarding the
* progress and status of the current Animation. Additionally, the id of the type of animation is
* passed. Within, the animationEventPerformed
method is called.
*
* @param cmd String Animation command passed instead of the current Status.
* @param description String description for messages.
*/
protected void animationAction(String cmd, String description) {
super.animationAction(AnimationEvent.ROTATION_DOUBLE_BST_ANIMATION, cmd, description, rotationLocation / 2.0);
}
/**
* Implements AnimationListener
which requires the following method.
* The only status of animation it listens for is Animation.ANIMATION_MESSAGE
, to pass
* the message on.
*
* @param e AnimationEvent that represents the information of the Animation.
*/
public void animationEventPerformed(AnimationEvent e) {
// set location each progress call.
rotationLocation = e.getProgress();
if (e.getStatus().equals(Animation.ANIMATION_MESSAGE)) {
messageAction(e.getAnimationDescription());
}
if (e.getStatus().equals(Animation.STEP)) {
animationAction(Animation.STEP, null);
}
}
}