
net.vectorpublish.desktop.vp.History Maven / Gradle / Ivy
/*
* Copyright (c) 2016, Peter Rader. All rights reserved.
* ___ ___ __ ______ __ __ __ __
* | | |.-----..----.| |_ .-----..----.| __ \.--.--.| |--.| ||__|.-----.| |--.
* | | || -__|| __|| _|| _ || _|| __/| | || _ || || ||__ --|| |
* \_____/ |_____||____||____||_____||__| |___| |_____||_____||__||__||_____||__|__|
*
* http://www.gnu.org/licenses/gpl-3.0.html
*/
package net.vectorpublish.desktop.vp;
import java.io.Serializable;
import java.util.Objects;
import javax.inject.Inject;
import net.vectorpublish.desktop.vp.api.InheritanceExclusion;
import net.vectorpublish.desktop.vp.api.history.ReadOnlyHistoryStepDataBean;
import net.vectorpublish.desktop.vp.api.io.Open;
import net.vectorpublish.desktop.vp.api.layer.Layer;
import net.vectorpublish.desktop.vp.api.ui.KeyframeSlider;
import net.vectorpublish.desktop.vp.api.vpd.DocumentNode;
import net.vectorpublish.desktop.vp.api.vpd.ModificationContext;
/**
* The History of the work on the Document. Every {@link DocumentNode} has a
* history, if you modify the {@link DocumentNode} the modifications are
*
* - executed via the {@link HistoryStep}
*
- stored in a serializable {@link ReadOnlyHistoryStepDataBean} and
*
- rolled back via the {@link HistoryStep}.
*
*/
public abstract class History implements InheritanceExclusion {
/**
* A {@link HistoryStep} holds all processes about what to do when the user
* click undo or redo. Additionally there is a {@link #data field} to store
* all Informations about what to store to a file on save. Store the
* document to a file will store all {@link ReadOnlyHistoryStepDataBean
* data-beans} to the File bound to the {@link DocumentNode}.
*
* It has two essentail fields, the next step and the previous step just
* like a double-linked-list. If the previous Step is null
you
* can not undo anymore, if the next step is null
you can not
* redo anymore.
*
*
* Rollback is the process of remove the work that has been done in the
* {@link DocumentNode#getLastExecutedHistoryStep() last executed step}, and
* point the
* {@link DocumentNode#setLastExecutedHistoryStep(History.HistoryStep) last
* executed} to the first previous step relative to the
* {@link DocumentNode#getLastExecutedHistoryStep() old last executed step}.
* Only if you {@link #canRollback() can rollback} the step, you are able to
* undo the work.
*
* Execute is the process of execute the work that must be done in the
* {@link #next first next step} that is realtive to the
* {@link DocumentNode#getLastExecutedHistoryStep() last executed step}.
* After the {@link #execute(ModificationContext) execution} of the work
* required in the next step, the
* {@link DocumentNode#setLastExecutedHistoryStep(History.HistoryStep) last
* step} will be set to the newly executed Step. Only if you
* {@link #canExecuteNext() can redo} the step, you are able to redo the
* work.
*/
public abstract class HistoryStep
implements InheritanceExclusion {
/**
* The {@link HistoryStep} that comes right before this
* {@link HistoryStep}. This field can be null
in case of
* the first HistorySteps.
*
* To create a {@link DocumentNode} is not a history-point, so every
* blank {@link DocumentNode} has not one {@link HistoryStep}.
*/
protected HistoryStep> previous;
/**
* The {@link Serializable} data the {@link HistoryStep} needs to work.
* How and what to do for the execution of the Step.
*/
protected final B data;
/**
* The {@link HistoryStep} that comes next. This field must be
* null
in the case that this is the last
* {@link HistoryStep}.
*/
protected HistoryStep> next;
/**
* Creates a new History. This instance is the very first
* {@link HistoryStep} of the chain of many.
*
*
* This constructor must be executed when the {@link DocumentNode} has
* no {@link HistoryStep}.
*
* @param data
* The Data for this {@link HistoryStep}.
*/
@Deprecated
public HistoryStep(B data) {
this.data = Objects.requireNonNull(data);
this.previous = null;
}
/**
* Adds a {@link HistoryStep} to existing chain of HistorySteps. To
* create a blank {@link DocumentNode} is not a {@link HistoryStep}.
*
* @param before
* The {@link HistoryStep} who comes right before this step
* would execute.
* @param data
* The Data for the HistoryStep.
*/
public HistoryStep(HistoryStep> before, B data) {
assert this.getClass().getDeclaredConstructors().length == 1 : "Should only have one constructor but has "
+ this.getClass().getDeclaredConstructors().length + "!";
this.previous = Objects.requireNonNull(before);
this.previous.next = this;
this.data = Objects.requireNonNull(data);
notifyHistoryChanged(before, this, Thread.currentThread().getName().equals(Open.THREAD_NAME_FILE_LOAD));
}
/**
* Checks if a next step is available for execution.
*
* After rollback this method returns true, even if the action who
* produced the history step to execute-next is not available.
*
* @return true
if a next step is available for execution,
* false
if not.
*/
public final boolean canExecuteNext() {
return next != null;
}
/**
* Returns if the last step executed can be rolled back.
*
* If false
is returned the document must be blank. If a
* new document is created this method returns false
.
*
* @return true
if the last step executed can be rolled
* back, false
if not.
*/
public final boolean canRollback() {
return previous != null;
}
/**
* Executes internally the atomic next {@link HistoryStep} in the
* {@link History}.
*
* There is no garantuee that {@link #rollback(ModificationContext)} has
* been called before!
*
* @param ctx
* The context to make modifications for the {@link Layer
* Layers}.
*/
protected abstract void execute(ModificationContext ctx);
/**
* Executes the complete next step in the {@link History}.
*/
public final void executeNext() {
final DocumentNode currentDocument = getCurrentDocument();
final ModificationContext ctx = new ModificationContext(History.this, currentDocument, layer, slider);
this.next.execute(ctx);
ctx.cleanUp();
currentDocument.setLastExecutedHistoryStep(this.next);
}
protected final B getData() {
return data;
}
/**
* The work to do to have the status of the previous {@link HistoryStep}
* .
*
* You have the 100% garatuee that at least one
* {@link #execute(ModificationContext)} has been called before!
*
* @param ctx
* The {@link ModificationContext}.
*/
protected abstract void rollback(ModificationContext ctx);
public final void rollbackToPrevious() {
final ModificationContext ctx = new ModificationContext(History.this, getCurrentDocument(), layer, slider);
rollback(ctx);
getCurrentDocument().setLastExecutedHistoryStep(previous);
ctx.cleanUp();
}
}
@Inject
private final Layer layer = null;
@Inject
private final KeyframeSlider slider = null;
public abstract DocumentNode getCurrentDocument();
protected abstract void notifyHistoryChanged(HistoryStep> previous, HistoryStep> newStep, boolean byFileRead);
}