Skip to content

Commit

Permalink
Fix weak listeners setup in jpro-file module
Browse files Browse the repository at this point in the history
  • Loading branch information
besidev committed Dec 20, 2024
1 parent 34a1af3 commit 69609b4
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 62 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
### 0.5.1-SNAPSHOT (TBD)

#### Bugfixes
* Fixed weak listeners setup in `jpro-file` module.

----------------------

### 0.5.0 (November 25, 2024)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ public WebFileDropper(Node node) {

// Wrap the listener into a WeakInvalidationListener to avoid memory leaks,
// that can occur if observers are not unregistered from observed objects after use.
final WeakInvalidationListener weakFileDragOverListener = new WeakInvalidationListener(fileDragOverListener);
multiFileUploader.fileDragOverProperty().addListener(weakFileDragOverListener);
multiFileUploader.fileDragOverProperty().addListener(new WeakInvalidationListener(fileDragOverListener));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package one.jpro.platform.file.picker;

import com.jpro.webapi.WebAPI;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
Expand Down Expand Up @@ -102,27 +100,14 @@ final ExtensionFilter findSelectedFilter() {
}
}

/**
* Synchronizes the selected {@link ExtensionFilter} between this file picker and the native {@link FileChooser}.
* This ensures that changes in one are reflected in the other without causing infinite update loops.
*
* @param fileChooser the native file chooser to synchronize with; must not be {@code null}
*/
final void synchronizeSelectedExtensionFilter(FileChooser fileChooser) {
fileChooser.selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(getNativeSelectedExtensionFilterChangeListener()));
selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(getSelectedExtensionFilterChangeListener(fileChooser)));
}

/**
* Creates a {@link ChangeListener} that listens for changes in the native {@link FileChooser}'s
* selected extension filter and updates the corresponding property in this file picker.
*
* @return a change listener for the native file chooser's selected extension filter
*/
@NotNull
private ChangeListener<FileChooser.ExtensionFilter> getNativeSelectedExtensionFilterChangeListener() {
final ChangeListener<FileChooser.ExtensionFilter> getNativeSelectedExtensionFilterChangeListener() {
return (observable, oldFilter, newFilter) -> {
if (updatingFromProperty) {
return;
Expand Down Expand Up @@ -152,7 +137,7 @@ private ChangeListener<FileChooser.ExtensionFilter> getNativeSelectedExtensionFi
* @return a change listener for the native file chooser's selected extension filter
*/
@NotNull
private ChangeListener<ExtensionFilter> getSelectedExtensionFilterChangeListener(FileChooser fileChooser) {
final ChangeListener<ExtensionFilter> getSelectedExtensionFilterChangeListener(FileChooser fileChooser) {
return (observable, oldFilter, newFilter) -> {
if (updatingFromFileChooser) {
return;
Expand Down Expand Up @@ -203,33 +188,4 @@ final ListChangeListener<ExtensionFilter> getNativeExtensionFilterListChangeList
}
};
}

/**
* Creates a {@link ListChangeListener} that listens for changes in the list of {@link ExtensionFilter}
* instances and updates the web-based file uploader's supported extensions accordingly.
* <p>
* This listener handles both additions and removals of extension filters.
* </p>
*
* @param multiFileUploader the web file uploader whose supported extensions will be updated; must not be {@code null}
* @return a list change listener for updating the web file uploader's supported extensions
*/
@NotNull
final ListChangeListener<ExtensionFilter> getWebExtensionFilterListChangeListener(WebAPI.MultiFileUploader multiFileUploader) {
return change -> {
while (change.next()) {
if (change.wasAdded()) {
for (ExtensionFilter extensionFilter : change.getAddedSubList()) {
extensionFilter.extensions()
.forEach(multiFileUploader.supportedExtensions()::add);
}
} else if (change.wasRemoved()) {
for (ExtensionFilter extensionFilter : change.getRemoved()) {
extensionFilter.extensions()
.forEach(multiFileUploader.supportedExtensions()::remove);
}
}
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.WeakListChangeListener;
import javafx.scene.Node;
import javafx.scene.control.SelectionMode;
import javafx.scene.input.MouseEvent;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import one.jpro.platform.file.ExtensionFilter;
import one.jpro.platform.file.FileSource;
import one.jpro.platform.file.NativeFileSource;
import one.jpro.platform.file.util.NodeUtils;
Expand All @@ -28,7 +32,13 @@
*/
public class NativeFileOpenPicker extends BaseFileOpenPicker {

private final FileChooser fileChooser;
private final FileChooser fileChooser = new FileChooser();
private final ChangeListener<FileChooser.ExtensionFilter> nativeSelectedExtensionFilterChangeListener =
getNativeSelectedExtensionFilterChangeListener();
private final ChangeListener<ExtensionFilter> selectedExtensionFilterChangeListener =
getSelectedExtensionFilterChangeListener(fileChooser);
private final ListChangeListener<ExtensionFilter> nativeExtensionFilterListChangeListener =
getNativeExtensionFilterListChangeListener(fileChooser);
private List<NativeFileSource> nativeFileSources = List.of();

/**
Expand All @@ -39,17 +49,13 @@ public class NativeFileOpenPicker extends BaseFileOpenPicker {
public NativeFileOpenPicker(Node node) {
super(node);

// Initialize the FileChooser
fileChooser = new FileChooser();

// Initializes synchronization between the FileChooser's selectedExtensionFilterProperty
// and the FilePicker's selectedExtensionFilter property.
synchronizeSelectedExtensionFilter(fileChooser);

// Wrap the listener into a WeakListChangeListener to avoid memory leaks,
// that can occur if observers are not unregistered from observed objects after use.
getExtensionFilters().addListener(
new WeakListChangeListener<>(getNativeExtensionFilterListChangeListener(fileChooser)));
getExtensionFilters().addListener(new WeakListChangeListener<>(nativeExtensionFilterListChangeListener));

// Define the action that should be performed when the user clicks on the node.
NodeUtils.addEventHandler(node, MouseEvent.MOUSE_CLICKED, actionEvent -> {
Expand Down Expand Up @@ -82,6 +88,19 @@ public NativeFileOpenPicker(Node node) {
});
}

/**
* Synchronizes the selected {@link ExtensionFilter} between this file picker and the native {@link FileChooser}.
* This ensures that changes in one are reflected in the other without causing infinite update loops.
*
* @param fileChooser the native file chooser to synchronize with; must not be {@code null}
*/
final void synchronizeSelectedExtensionFilter(FileChooser fileChooser) {
fileChooser.selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(nativeSelectedExtensionFilterChangeListener));
selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(selectedExtensionFilterChangeListener));
}

@Override
public final String getTitle() {
return fileChooser.getTitle();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.collections.ListChangeListener;
import javafx.collections.WeakListChangeListener;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.stage.FileChooser;
import javafx.stage.Window;
import one.jpro.platform.file.ExtensionFilter;

import java.io.File;
import java.util.concurrent.CompletableFuture;
Expand All @@ -22,22 +26,37 @@
*/
public class NativeFileSavePicker extends BaseFileSavePicker {

private final FileChooser fileChooser;
private final FileChooser fileChooser = new FileChooser();
private final ChangeListener<FileChooser.ExtensionFilter> nativeSelectedExtensionFilterChangeListener =
getNativeSelectedExtensionFilterChangeListener();
private final ChangeListener<ExtensionFilter> selectedExtensionFilterChangeListener =
getSelectedExtensionFilterChangeListener(fileChooser);
private final ListChangeListener<ExtensionFilter> nativeExtensionFilterListChangeListener =
getNativeExtensionFilterListChangeListener(fileChooser);

public NativeFileSavePicker(Node node) {
super(node);

// Initialize the FileChooser
fileChooser = new FileChooser();

// Initializes synchronization between the FileChooser's selectedExtensionFilterProperty
// and the FilePicker's selectedExtensionFilter property.
synchronizeSelectedExtensionFilter(fileChooser);

// Wrap the listener into a WeakListChangeListener to avoid memory leaks,
// that can occur if observers are not unregistered from observed objects after use.
getExtensionFilters().addListener(
new WeakListChangeListener<>(getNativeExtensionFilterListChangeListener(fileChooser)));
getExtensionFilters().addListener(new WeakListChangeListener<>(nativeExtensionFilterListChangeListener));
}

/**
* Synchronizes the selected {@link ExtensionFilter} between this file picker and the native {@link FileChooser}.
* This ensures that changes in one are reflected in the other without causing infinite update loops.
*
* @param fileChooser the native file chooser to synchronize with; must not be {@code null}
*/
final void synchronizeSelectedExtensionFilter(FileChooser fileChooser) {
fileChooser.selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(nativeSelectedExtensionFilterChangeListener));
selectedExtensionFilterProperty()
.addListener(new WeakChangeListener<>(selectedExtensionFilterChangeListener));
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,20 @@
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.ListChangeListener;
import javafx.collections.WeakListChangeListener;
import javafx.scene.Node;
import javafx.scene.control.SelectionMode;
import one.jpro.platform.file.ExtensionFilter;
import one.jpro.platform.file.FileSource;
import one.jpro.platform.file.WebFileSource;
import one.jpro.platform.file.util.NodeUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

/**
Expand All @@ -27,6 +31,7 @@
public class WebFileOpenPicker extends BaseFileOpenPicker {

private final WebAPI.MultiFileUploader multiFileUploader;
private final ListChangeListener<ExtensionFilter> webExtensionFilterListChangeListener;
private List<WebFileSource> webFileSources = List.of();

/**
Expand All @@ -41,10 +46,17 @@ public WebFileOpenPicker(Node node) {
WebAPI.makeMultiFileUploadNodeStatic(node));
multiFileUploader.setSelectFileOnClick(true);

webExtensionFilterListChangeListener = change -> {
final List<String> supportedExtensionsList = change.getList().stream()
.flatMap(ext -> ext.extensions().stream())
.toList();
final Set<String> supportedExtensionsSet = new HashSet<>(supportedExtensionsList); // Remove duplicates
multiFileUploader.supportedExtensions().setAll(supportedExtensionsSet.stream().toList());
};

// Wrap the listener into a WeakListChangeListener to avoid memory leaks,
// that can occur if observers are not unregistered from observed objects after use.
getExtensionFilters().addListener(
new WeakListChangeListener<>(getWebExtensionFilterListChangeListener(multiFileUploader)));
getExtensionFilters().addListener(new WeakListChangeListener<>(webExtensionFilterListChangeListener));
}

// title property
Expand Down

0 comments on commit 69609b4

Please sign in to comment.