
com.dlsc.preferencesfx.history.Change Maven / Gradle / Ivy
Show all versions of preferencesfx-core Show documentation
package com.dlsc.preferencesfx.history;
import com.dlsc.preferencesfx.model.Setting;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Callable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ListProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Represents a change, which is comprised of a new and an old value.
*
* @param the data type of the change, which is reflected in a {@link ListProperty} for a
* list change and in a {@link ObjectProperty} for regular changes as well
* @author François Martin
* @author Marco Sanfratello
* @implNote There are two types: List changes and regular changes. Internally, all changes are
* saved into a {@link ListProperty} for the old and the new value, regardless of the
* type of change. However, if two {@link ObservableList} are used to create a change, a
* boolean flags the change as a list change. In case of a regular change, a binding will
* also set an {@link ObjectProperty} for easier handling.
*/
public class Change
{
private static final Logger LOGGER =
LoggerFactory.getLogger(Change.class.getName());
protected final Setting setting;
private final ListProperty
oldList = new SimpleListProperty<>();
private final ListProperty
newList = new SimpleListProperty<>();
private final ObjectProperty
oldValue = new SimpleObjectProperty<>();
private final ObjectProperty
newValue = new SimpleObjectProperty<>();
private final BooleanProperty listChange = new SimpleBooleanProperty();
private final LocalDateTime timestamp;
/**
* Constructs a generalized change.
*
* @param setting the setting that was changed
* @param listChange true if this is a list change
*/
protected Change(Setting setting, boolean listChange) {
this.setting = setting;
this.listChange.set(listChange);
timestamp = LocalDateTime.now();
setupBindings();
}
/**
* Constructs a list change.
*
* @param setting the setting that was changed
* @param oldList the "before" value(s) of the change
* @param newList the "after" value(s) of the change
*/
public Change(Setting setting, ObservableList
oldList, ObservableList
newList) {
this(setting, true);
this.oldList.set(FXCollections.observableArrayList(oldList));
this.newList.set(FXCollections.observableArrayList(newList));
}
/**
* Constructs a regular object change.
*
* @param setting the setting that was changed
* @param oldValue the "before" value of the change
* @param newValue the "after" value of the change
*/
public Change(Setting setting, P oldValue, P newValue) {
this(setting, false);
this.oldList.set(FXCollections.observableArrayList(Arrays.asList(oldValue)));
this.newList.set(FXCollections.observableArrayList(Arrays.asList(newValue)));
}
private void setupBindings() {
// oldValue will represent the object contained in oldList, if this is not a listChange
oldValue.bind(
Bindings.createObjectBinding(createListToObjectBinding(oldList), oldList, listChange)
);
// newValue will represent the object contained in newList, if this is not a listChange
newValue.bind(
Bindings.createObjectBinding(createListToObjectBinding(newList), newList, listChange)
);
}
/**
* Creates a function, which handles binding between a ListProperty and an ObjectProperty.
*
*
If this change isn't a list change, oldValue and newValue properties will have the single
* element inside of the list, for easier usage.
*
* @param listProperty to be bound to the object property
* @return Callable function, which binds a list to an object property.
*/
private Callable createListToObjectBinding(ListProperty
listProperty) {
return () -> {
if (!isListChange() && listProperty.get() != null && listProperty.get().size() != 0) {
return listProperty.get().get(0);
}
return null;
};
}
/**
* Compares newValue and oldValue to see if they are the same.
* If this is the case, this change is redundant, since it doesn't represent a true change.
* This can happen on compounded changes.
*
* @return true if redundant, else if otherwise.
*/
public boolean isRedundant() {
if (isListChange()) {
return Objects.equals(oldList.get(),newList.get());
}
return oldValue.get().equals(newValue.get());
}
/**
* Undos a change.
* Does this by setting the corresponding value of the {@link Setting} to the old value of this
* change.
*/
public void undo() {
if (isListChange()) {
LOGGER.trace("Undoing list change: " + oldList.get().toString());
setting.valueProperty().setValue(oldList.get());
} else {
setting.valueProperty().setValue(oldValue.get());
}
}
/**
* Redos a change.
* Does this by setting the corresponding value of the {@link Setting} to the new value of this
* change.
*/
public void redo() {
if (isListChange()) {
LOGGER.trace("Redoing list change: " + newList.get().toString());
setting.valueProperty().setValue(newList.get());
} else {
setting.valueProperty().setValue(newValue.get());
}
}
public ObservableList
getOldList() {
return oldList.get();
}
public ObservableList
getNewList() {
return newList.get();
}
public void setNewList(ObservableList
newList) {
LOGGER.trace("Setting new List, old: " + oldList.toString() + " new: " + newList.toString());
this.newList.set(FXCollections.observableArrayList(newList));
}
public boolean isListChange() {
return listChange.get();
}
public ReadOnlyBooleanProperty listChangeProperty() {
return listChange;
}
public P getOldValue() {
return oldValue.get();
}
public P getNewValue() {
return newValue.get();
}
public void setNewValue(P newValue) {
this.newList.set(FXCollections.observableArrayList(Arrays.asList(newValue)));
}
public Setting getSetting() {
return setting;
}
public String getTimestamp() {
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);
return timestamp.format(formatter);
}
public ReadOnlyObjectProperty
oldValueProperty() {
return oldValue;
}
public ReadOnlyObjectProperty
newValueProperty() {
return newValue;
}
public ReadOnlyListProperty
oldListProperty() {
return oldList;
}
public ReadOnlyListProperty
newListProperty() {
return newList;
}
}