Command history manager
This library provides advanced history management functionality for Java.
This package focuses strictly on history handling and includes no supplementary tools.
Usage
Here is an example with the built-in SampleDocument:
History history = new LinearHistory();
SampleDocument document = new SampleDocument(history);
document.printChars('A', 'B');
System.out.println(document); // A B
Command abCommand = history.getPrevious();
document.printChar('C');
document.moveTo(1);
document.printChar('X');
System.out.println(document); // A X|B C
history.rollBackPrevious();
System.out.println(document); // A|B C
document.moveToEnd();
document.printChar('D');
System.out.println(document); // A B C D|
history.moveAfter(abCommand);
System.out.println(document); // A B|
history.executeNext();
System.out.println(document); // A B C|
history.executeNext();
System.out.println(document); // A B C D|
Output:
A B|
A X|B C
A|B C
A B C D|
A B|
A B C|
A B C D|
When you develop your custom history functionality you need two general things: a history manager and a set of possible commands. This library provides three types of history managers:
SingleHistory
: minimal implementation, it can store only one commandLinearHistory
: this is the traditional implementation, stores a single timeline of commandsComplexHistory
: an advanced history manager, can store a tree of commands
LinearHistory
and ComplexHistory
support maximum capacity to avoid excessive memory allocation.
Of course you can build a custom history manager too, in this case you must implement the History
interface.
Commands must implement the Command
interface, and it is recommended to extend AbstractCommand
. For example:
class AppendToStringListCommand extends AbstractCommand {
private final List<String> list;
private final String item;
public AppendToStringListCommand(List<String> list, String item) {
this.list = list;
this.item = item;
}
@Override
protected boolean _execute() {
list.add(item);
return true;
}
@Override
protected boolean _rollBack() {
list.remove(list.size() - 1);
return true;
}
}
History history = new ComplexHistory();
List<String> list = new LinkedList<String>();
history.addAndExecute(new AppendToStringListCommand(list, "first"));
System.out.println(list); // [first]
Command deadSecondCommand = new AppendToStringListCommand(list, "second");
history.addAndExecute(deadSecondCommand);
System.out.println(list); // [first, second]
history.rollBackPrevious();
System.out.println(list); // [first]
history.addAndExecute(new AppendToStringListCommand(list, "third"));
System.out.println(list); // [first, third]
CommandAggregation fourthFifthAndSixth = new CommandAggregation();
fourthFifthAndSixth.add(new AppendToStringListCommand(list, "fourth"));
fourthFifthAndSixth.add(new AppendToStringListCommand(list, "fifth"));
fourthFifthAndSixth.add(new AppendToStringListCommand(list, "sixth"));
fourthFifthAndSixth.close();
history.addAndExecute(fourthFifthAndSixth);
System.out.println(list); // [first, third, fourth, fifth, sixth]
history.rollBackPrevious();
System.out.println(list); // [first, third]
history.moveAfter(deadSecondCommand);
System.out.println(list); // [first, second]
Output:
[first]
[first, second]
[first]
[first, third]
[first, third, fourth, fifth, sixth]
[first, third]
[first, second]
The last history.moveAfter(deadSecondCommand);
moves pointer to a dead command (not on the current timeline). This would not have worked with LinearHistory
.