diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java index 34156bf51..7158bdc3f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandler.java @@ -46,11 +46,14 @@ final class WelcomeDialogFilesDropHandler { private final List droppedFiles; private final List toOpen; + private final List unsupportedItems; private Consumer> openFiles; + private Consumer> handleUnsupported; WelcomeDialogFilesDropHandler(List droppedFiles) { this.droppedFiles = Objects.requireNonNull(droppedFiles); this.toOpen = new ArrayList<>(droppedFiles.size()); + this.unsupportedItems = new ArrayList<>(droppedFiles.size()); } final WelcomeDialogFilesDropHandler withSupportedFiles(Consumer> handleOpen) { @@ -58,6 +61,11 @@ final WelcomeDialogFilesDropHandler withSupportedFiles(Consumer> ha return this; } + final WelcomeDialogFilesDropHandler withUnsupportedFiles(Consumer> unsupportedHandler) { + this.handleUnsupported = unsupportedHandler; + return this; + } + final void run() { analyzeDroppedItems(); handleDropResult(); @@ -67,10 +75,16 @@ final void handleDropResult() { if (this.openFiles == null) { throw new IllegalStateException("Please configure a dropped file handling action using the withSupportedFiles(...) method."); } + if (this.handleUnsupported == null) { + throw new IllegalStateException("Please configure an action for handling of unsupported files using withUnsupportedFiles(...) method."); + } if (!toOpen.isEmpty()) { LOGGER.log(Level.INFO, "Received drop event to open files..."); openFiles.accept(toOpen); + } else { + LOGGER.log(Level.INFO, "Dropped object does not contain any loadable FXML files."); + handleUnsupported.accept(unsupportedItems); } } @@ -81,17 +95,15 @@ final void analyzeDroppedItems() { for (var file : droppedFiles) { if (file.isDirectory()) { - LOGGER.log(Level.INFO, "Dropped object is a directory: {0}", file.getAbsolutePath()); File[] children = file.listFiles(); List inDir = new ArrayList<>(children.length); for (var child : children) { if (isFxml(child)) { - LOGGER.log(Level.INFO, "FXML file found: {0}", child.getAbsolutePath()); inDir.add(child.getAbsolutePath()); } } if (inDir.isEmpty()) { - LOGGER.log(Level.INFO, "Dropped directory does not contain FXML files: {0}", file.getAbsolutePath()); + unsupportedItems.add(file.getAbsolutePath()); } else { toOpen.addAll(inDir); } @@ -99,7 +111,7 @@ final void analyzeDroppedItems() { if (isFxml(file)) { toOpen.add(file.getAbsolutePath()); } else { - LOGGER.log(Level.INFO, "Dropped item is not a FXML file: {0}", file.getAbsolutePath()); + unsupportedItems.add(file.getAbsolutePath()); } } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java index c21d52c82..b03b27856 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogWindowController.java @@ -131,10 +131,21 @@ void handleDroppedFiles(DragEvent event) { if (event.getDragboard().hasFiles()) { new WelcomeDialogFilesDropHandler(event.getDragboard().getFiles()) .withSupportedFiles(fileNames->Platform.runLater(()->handleOpen(fileNames))) + .withUnsupportedFiles(unsupported->notifyUserWhenDroppedUnsupportedFiles(unsupported)) .run(); } } + private void notifyUserWhenDroppedUnsupportedFiles(List unsupported) { + ErrorDialog dialog = new ErrorDialog(getStage()); + dialog.setTitle(I18N.getString("welcome.loading.when.dropped.error.title")); + dialog.setMessage(I18N.getString("welcome.loading.when.dropped.error.message")); + String detail = unsupported.stream() + .collect(Collectors.joining(System.lineSeparator())); + dialog.setDetails(detail); + Platform.runLater(()->dialog.showAndWait()); + } + @Override protected void controllerDidLoadFxml() { super.controllerDidLoadFxml(); diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties index d0e169e1a..543a2cd43 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/i18n/SceneBuilderApp.properties @@ -511,6 +511,8 @@ welcome.recent.items.loading = Loading recent projects... welcome.recent.items.no.recent.items = no recent projects welcome.open.project.label = Open Project welcome.loading.label = Loading Components... +welcome.loading.when.dropped.error.title=Unsupported file format or empty directory +welcome.loading.when.dropped.error.message=The dropped object is either not a JavaFX FXML file or does not contain any FXML files to be loaded. # -- Template (this keys are replicated from SceneBuilderKit.properties) template.new.project.label = New Project from Template diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandlerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandlerTest.java index 6e3d16721..d07800f62 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandlerTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/welcomedialog/WelcomeDialogFilesDropHandlerTest.java @@ -38,7 +38,9 @@ import java.io.File; import java.nio.file.Path; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.*; @@ -64,6 +66,9 @@ void that_exception_is_raised_with_incomplete_configuration() { assertThrows(IllegalStateException.class, ()->classUnderTest.run()); classUnderTest.withSupportedFiles(files->System.out.println(files)); + assertThrows(IllegalStateException.class, ()->classUnderTest.run()); + + classUnderTest.withUnsupportedFiles(unsupported->System.out.println(unsupported)); assertDoesNotThrow(()->classUnderTest.run()); } @@ -81,10 +86,11 @@ void that_list_of_fxml_files_will_passed_to_open_action() { // Action handler to notify user on unsupported items List unsupportedFiles = new ArrayList<>(); - + Consumer> unsupportedFileHandling = unsupported->unsupportedFiles.addAll(unsupported); classUnderTest = new WelcomeDialogFilesDropHandler(droppedFiles) - .withSupportedFiles(openFilesAction); + .withSupportedFiles(openFilesAction) + .withUnsupportedFiles(unsupportedFileHandling); assertDoesNotThrow(()->classUnderTest.run()); assertEquals(2, fileOpenResults.size()); @@ -93,31 +99,63 @@ void that_list_of_fxml_files_will_passed_to_open_action() { } @Test - void that_dropped_subdirectories_are_searched_for_fxml_in_first_level(@TempDir Path fxmlDir) { - File emptyDirectory = new File("src/main/resources"); - File unsupportedFile = new File("src/main/resources/com/oracle/javafx/scenebuilder/app/SceneBuilderLogo_32.png"); + void that_an_attempt_to_handle_unsupported_files_triggers_appropriate_action(@TempDir Path emptyDir) throws Exception { + List droppedFiles = List.of(new File("Image.png"), emptyDir.toFile()); + + // Action handler for opening files + List fileOpenResults = new ArrayList<>(); + Consumer> openFilesAction = files->fileOpenResults.addAll(files); + + // Action handler to notify user on unsupported items + List unsupportedFiles = new ArrayList<>(); + Consumer> unsupportedFileHandling = unsupported->{ + for (String file : unsupported) { + var item = new File(file); + if (item.isDirectory()) { + unsupportedFiles.add(new File(file).getName() + "(dir is empty)"); + } else { + unsupportedFiles.add(new File(file).getName()); + } + } + }; - List droppedFiles = List.of(emptyDirectory, - new File("src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog"), + classUnderTest = new WelcomeDialogFilesDropHandler(droppedFiles) + .withSupportedFiles(openFilesAction) + .withUnsupportedFiles(unsupportedFileHandling); + classUnderTest.run(); + + assertTrue(fileOpenResults.isEmpty()); + assertEquals(2, unsupportedFiles.size()); + assertEquals("Image.png", unsupportedFiles.get(0)); + assertTrue(unsupportedFiles.get(1).endsWith("(dir is empty)")); + } + + @Test + void that_dropped_subdirectories_are_searched_for_fxml_in_first_level(@TempDir Path fxmlDir) { + List droppedFiles = List.of(new File("src/main/resources/com/oracle/javafx/scenebuilder/app/welcomedialog"), new File("src/main/resources/com/oracle/javafx/scenebuilder/app/DocumentWindow.fxml"), - unsupportedFile); + new File("src/main/resources/com/oracle/javafx/scenebuilder/app/SceneBuilderLogo_32.png")); // Action handler for opening files - List fileOpenResults = new ArrayList<>(); + Set fileOpenResults = new HashSet<>(); Consumer> openFilesAction = files->{ for (String file : files) { fileOpenResults.add("opened " + new File(file).getName()); } }; + // Action handler to notify user on unsupported items + List unsupportedFiles = new ArrayList<>(); + Consumer> unsupportedFileHandling = unsupported->unsupportedFiles.addAll(unsupported); classUnderTest = new WelcomeDialogFilesDropHandler(droppedFiles) - .withSupportedFiles(openFilesAction); + .withSupportedFiles(openFilesAction) + .withUnsupportedFiles(unsupportedFileHandling); classUnderTest.run(); assertTrue(fileOpenResults.contains("opened WelcomeWindow.fxml"), "FXML from dropped directory"); assertTrue(fileOpenResults.contains("opened DocumentWindow.fxml"), "FXML file dropped"); - assertEquals(2, fileOpenResults.size()); + assertTrue(unsupportedFiles.isEmpty()); } }