
com.dlsc.gemsfx.binding.AbstractChangeTracker Maven / Gradle / Ivy
package com.dlsc.gemsfx.binding;
import javafx.beans.Observable;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.WeakListChangeListener;
import java.util.Objects;
import java.util.function.Consumer;
/**
* This abstract class provides a framework for tracking changes in an ObservableList of Observable elements.
* It encapsulates common logic for adding and removing change listeners to the observable elements
* within the list and provides a mechanism to notify changes through a specified Consumer.
*
* @param the type of Observable elements in the list that is being tracked
*/
public abstract class AbstractChangeTracker {
private final ObservableList source;
private final ListChangeListener outerListChangeListener = change -> {
while (change.next()) {
if (change.wasRemoved()) {
change.getRemoved().forEach(this::safeRemoveListener);
}
if (change.wasAdded()) {
change.getAddedSubList().forEach(this::safeAddListener);
}
notifyChange();
}
};
private final WeakListChangeListener weakOuterListChangeListener = new WeakListChangeListener<>(outerListChangeListener);
protected Consumer> onChanged;
/**
* Constructs an AbstractChangeTracker with the provided source list.
*
* @param source the observable list of elements that is being monitored
*/
public AbstractChangeTracker(ObservableList source) {
this(source, null);
}
/**
* Constructs an AbstractChangeTracker with the provided source list and change handler.
*
* @param source the observable list of elements that is being monitored
* @param onChanged the consumer to handle change notifications
*/
public AbstractChangeTracker(ObservableList source, Consumer> onChanged) {
this.source = Objects.requireNonNull(source, "Source list cannot be null");
// Consumer is allowed to be null
this.onChanged = onChanged;
}
/**
* Initializes the listener for the outer list.
* This method should be called after subclass constructors have fully initialized any necessary fields to ensure
* that listeners interact with a fully initialized object.
*/
protected void initOuterListener() {
this.source.forEach(this::safeAddListener);
this.source.addListener(weakOuterListChangeListener);
}
/**
* Sets the consumer that will be notified when changes occur.
*
* @param onChanged the consumer to be notified of changes
*/
public void setOnChanged(Consumer> onChanged) {
this.onChanged = onChanged;
}
protected void notifyChange() {
if (onChanged != null) {
onChanged.accept(source);
}
}
protected abstract void safeAddListener(T value);
protected abstract void safeRemoveListener(T value);
/**
* Disposes this tracker by removing all listeners from the source and nested lists.
* It is recommended to call this method when the tracker is no longer needed.
* Doing so helps ensure that resources are properly released and helps prevent potential memory leaks.
* Calling this method is particularly important in environments with limited resources or
* in applications that create and dispose many trackers over their lifetime.
*/
public void dispose() {
source.forEach(this::safeRemoveListener);
source.removeListener(weakOuterListChangeListener);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy