/*
* funCKit - functional Circuit Kit
* Copyright (C) 2013 Lukas Elsner <open@mindrunner.de>
* Copyright (C) 2013 Peter Dahlberg <catdog2@tuxzone.org>
* Copyright (C) 2013 Julian Stier <mail@julian-stier.de>
* Copyright (C) 2013 Sebastian Vetter <mail@b4sti.eu>
* Copyright (C) 2013 Thomas Poxrucker <poxrucker_t@web.de>
* Copyright (C) 2013 Alexander Treml <alex.treml@directbox.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.sep2011.funckit.util.command;
import de.sep2011.funckit.util.Log;
import java.util.ArrayList;
import java.util.List;
import static com.google.common.base.Preconditions.checkArgument;
/**
* Dispatches {@link Command}s and adds them to a History to be able to undo,
* redo them.
*/
public class CommandDispatcher {
private List<Command> commands;
private int limit;
private int lastExecutedCommand;
/**
* Create a new {@link CommandDispatcher} with a maximum size history.
*/
public CommandDispatcher() {
initialize(Integer.MAX_VALUE);
}
/**
* Create a new {@link CommandDispatcher}.
*
* @param limit
* length of the History
*/
public CommandDispatcher(int limit) {
initialize(limit);
}
/**
* Initializes object with all needed parameters.
*
* @param limit
*/
private void initialize(int limit) {
checkArgument(limit >= 0, "Limit must be >= 0");
this.limit = limit;
commands = new ArrayList<Command>();
lastExecutedCommand = -1;
}
/**
* Returns the current set limit for this command dispatcher.
*
* @return the limit
*/
public int getLimit() {
return limit;
}
/**
* Specifies a limit of possible commands for this dispatcher.
*
* @param limit
* use Integer.MAX_VALUE for unlimited history, value must be
* non-negative
*/
public void setLimit(int limit) {
checkArgument(limit >= 0, "Limit must be >= 0");
this.limit = limit;
removeOldCommands();
}
/**
* Remove old commands as long as history size exceeds limit.
*/
private void removeOldCommands() {
while (commands.size() >= limit) {
commands.remove(0);
lastExecutedCommand--;
}
}
/**
* Dispatches a new command.
*
* @param command
* Command object to execute, should not be null
*/
public void dispatch(Command command) {
removeOldCommands();
/* Dispatch new command and save it in history. */
commands.add(lastExecutedCommand + 1, command);
lastExecutedCommand++;
/*
* Remove all commands after the newly added command as they are invalid
* then
*/
int beginObsolete = commands.indexOf(command) + 1;
int endObsolete = commands.size() - 1;
// Log.gl().debug("Removing commands " + beginObsolete + " to " +
// endObsolete);
for (int i = endObsolete; i >= beginObsolete; --i) {
commands.remove(i);
}
command.execute();
if (!command.isExecuted()) {
Log.gl().debug("Removing not executed command");
commands.remove(command);
lastExecutedCommand--;
}
}
/**
* Test if the Dispatcher is able to step Back one {@link Command} in the
* history.
*
* @return true if the Dispatcher is able to step Back one {@link Command}
* in the history
*/
public boolean canStepBack() {
return (lastExecutedCommand > -1);
}
/**
* Test if the Dispatcher is able to step forward one {@link Command} in the
* history.
*
* @return true if the Dispatcher is able to step forward one
* {@link Command} in the history
*/
public boolean canStepForward() {
return (lastExecutedCommand < (commands.size() - 1));
}
/**
* Undoes a previously executed command.
*/
public void stepBack() {
/* Only step back in history if last executed command exists. */
if (lastExecutedCommand > -1) {
Command command = commands.get(lastExecutedCommand);
lastExecutedCommand--;
Log.gl().debug("stepping backwards");
command.undo();
}
}
/**
* Executes a command, that was previously performed and then undone.
*/
public void stepForward() {
/*
* Only step forward in history if an index in command list greater than
* last executed command exists.
*/
if (lastExecutedCommand < (commands.size() - 1)) {
Command command = commands.get(lastExecutedCommand + 1);
lastExecutedCommand++;
Log.gl().debug("stepping forward");
command.execute();
}
}
/**
* Undoes all commands dispatched by this command dispatcher.
*/
public void rewind() {
/*
* Step back as long as lastExecutedCommand exists (index greater or
* equal zero).
*/
while (lastExecutedCommand > -1) {
stepBack();
}
}
/**
* Redoes all commands in the Queue.
*/
public void replay() {
while (canStepForward()) {
stepForward();
}
}
/**
* Clears the complete history of this {@link CommandDispatcher}.
*/
public void clear() {
commands.clear();
lastExecutedCommand = -1;
}
}