/*
* Copyright James Leigh (c) 2007.
*
* Licensed under the Aduna BSD-style license.
*/
package net.enilink.komma.dm.change;
import java.util.List;
import java.util.Stack;
import net.enilink.komma.dm.IDataManager;
/**
* Tracks changes to an {@link IDataManager} and allows those changes to be
* reversed.
*
*/
public class DataChangeStack extends DataChangeTracker implements
IDataChangeStack {
private boolean trackChanges = true;
private int saveLocation = 0;
private int undoLimit = 0;
private Stack<List<IDataChange>> undoStack = new Stack<List<IDataChange>>(),
redoStack = new Stack<List<IDataChange>>();
public void flush() {
redoStack.clear();
undoStack.clear();
saveLocation = 0;
}
public int getUndoLimit() {
return undoLimit;
}
@Override
protected void handleChanges(List<IDataChange> committed) {
if (trackChanges) {
pushChanges(committed);
}
super.handleChanges(committed);
}
public boolean isDirty() {
return undoStack.size() != saveLocation;
}
public void markSaveLocation() {
saveLocation = undoStack.size();
}
public void pushChanges(List<IDataChange> changes) {
if (getUndoLimit() > 0) {
while (undoStack.size() >= getUndoLimit()) {
undoStack.remove(0);
if (saveLocation > -1)
saveLocation--;
}
}
if (saveLocation > undoStack.size())
saveLocation = -1; // The save point was somewhere in the redo stack
undoStack.push(changes);
}
private void redo(List<IDataChange> changes, IDataManager dm) {
for (IDataChange change : changes) {
change.redo(dm);
}
}
public void redo(IDataManager dm) {
try {
trackChanges = false;
synchronized (activeDataManagers) {
boolean active = dm.getTransaction().isActive();
if (!active) {
dm.getTransaction().begin();
}
if (!redoStack.isEmpty()) {
redo(redoStack.pop(), dm);
}
List<IDataChange> activeChanges = activeDataManagers.get(dm);
if (activeChanges != null) {
redo(activeChanges, dm);
}
if (!active) {
dm.getTransaction().commit();
}
}
} finally {
trackChanges = true;
}
}
public void setUndoLimit(int undoLimit) {
this.undoLimit = undoLimit;
}
private void undo(List<IDataChange> changes, IDataManager dm) {
for (int i = changes.size() - 1; i >= 0; i--) {
IDataChange change = changes.get(i);
change.undo(dm);
}
}
public void undo(IDataManager dm) {
try {
trackChanges = false;
synchronized (activeDataManagers) {
boolean active = dm.getTransaction().isActive();
if (!active) {
dm.getTransaction().begin();
}
List<IDataChange> activeChanges = activeDataManagers.get(dm);
if (activeChanges != null) {
undo(activeChanges, dm);
}
if (!undoStack.isEmpty()) {
List<IDataChange> changes = undoStack.pop();
undo(changes, dm);
redoStack.add(changes);
}
if (!active) {
dm.getTransaction().commit();
}
}
} finally {
trackChanges = true;
}
}
}