/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* eAdventure 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package es.eucm.ead.editor.control.commands;
import java.util.Arrays;
import java.util.List;
import es.eucm.ead.editor.control.Command;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Stack;
import es.eucm.ead.editor.model.EditorModel;
import es.eucm.ead.editor.model.ModelEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import es.eucm.ead.editor.model.MergeableModelChange;
/**
* Class that handles multiple commands in a list as a single one.
* Commands will be executed in order of addition (first added first), and will
* be undone in reverse order.
*/
public class CompositeCommand extends Command {
private static final Logger logger = LoggerFactory
.getLogger(CompositeCommand.class);
protected String commandName;
/**
* The list of Command objects to be treated as a single one
*/
private List<Command> commandList;
/**
* @param list Command objects to be treated as a single one
*/
public CompositeCommand(List<Command> list) {
this.commandList = list;
commandName = generateName();
}
/**
* @param comms array of Command objects to be treated as a single one
*/
public CompositeCommand(Command... comms) {
this.commandList = Arrays.asList(comms);
commandName = generateName();
}
private String generateName() {
StringBuilder sb = new StringBuilder("Multiple[");
for (Command c : commandList) {
sb.append(c).append(' ');
}
sb.append("]");
return sb.toString();
}
/**
* Performs this list of commands. Failure in any triggers an
* undo of previously-completed list-commands.
*/
@Override
public ModelEvent performCommand(EditorModel em) {
Stack<Command> done = new Stack<Command>();
MergeableModelChange mmc = new MergeableModelChange(commandName, this);
for (Command c : commandList) {
ModelEvent me = c.performCommand(em);
if (me != null) {
done.push(c);
mmc.merge(me);
} else {
// undo in reverse order
while (!done.empty()) {
done.pop().undoCommand(em);
}
return null;
}
}
mmc.commit();
return mmc;
}
/**
* Returns true if all commands in the list can be undone
*/
@Override
public boolean canUndo() {
for (Command c : commandList) {
if (!c.canUndo()) {
return false;
}
}
return true;
}
/**
* Undoes all commands in this list. Failure to undo any triggers
* a redo of previously-completed undos.
*/
@Override
public ModelEvent undoCommand(EditorModel em) {
Stack<Command> undone = new Stack<Command>();
// use reverse order (last-to-first)
MergeableModelChange mmc = new MergeableModelChange(commandName, this);
for (Command c : new ListReverser<Command>(commandList)) {
ModelEvent me = c.undoCommand(em);
if (me != null) {
undone.push(c);
mmc.merge(me);
} else {
logger.warn("error undoing {}", c);
// redo in reverse order
while (!undone.empty()) {
undone.pop().undoCommand(em);
}
return null;
}
}
mmc.commit();
return mmc;
}
/**
* Returns true if all commands in the list can be redone
*/
@Override
public boolean canRedo() {
for (Command c : commandList) {
if (!c.canRedo()) {
return false;
}
}
return true;
}
/**
* Repeats all commands in this list. Failure to undo any triggers
* a undo of previously-completed redos.
*/
@Override
public ModelEvent redoCommand(EditorModel em) {
ArrayList<Command> redone = new ArrayList<Command>();
MergeableModelChange mmc = new MergeableModelChange(commandName, this);
for (Command c : commandList) {
ModelEvent me = c.redoCommand(em);
if (me != null) {
mmc.merge(me);
} else {
logger.warn("error redoing {}", c);
for (Command good : redone) {
good.redoCommand(em);
}
return null;
}
}
mmc.commit();
return mmc;
}
/**
* Combines this command with another. Essentially a NOP, as we do not
* want to combine command-lists
*/
@Override
public boolean combine(Command other) {
return false;
}
private static class ListReverser<T> implements Iterable<T> {
private ListIterator<T> listIterator;
public ListReverser(List<T> wrappedList) {
this.listIterator = wrappedList.listIterator(wrappedList.size());
}
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
@Override
public boolean hasNext() {
return listIterator.hasPrevious();
}
@Override
public T next() {
return listIterator.previous();
}
@Override
public void remove() {
throw new UnsupportedOperationException("Not supported");
}
};
}
}
}