From 023d86a19ab256af428334dbac93cb13094719f1 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 22 Dec 2021 20:48:36 +0100 Subject: [PATCH 01/42] Application data, User Library and Preferences are now stored specific to each SceneBuilder version. Thus multiple versions can run in parallel. --- .../javafx/scenebuilder/app/AppPlatform.java | 43 ++++--- .../preferences/PreferencesController.java | 7 +- .../scenebuilder/app/AppPlatformTest.java | 106 ++++++++++++++++++ .../PreferencesControllerTest.java | 58 ++++++++++ 4 files changed, 199 insertions(+), 15 deletions(-) create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 3d7e77235..7e5d96d7f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, Gluon and/or its affiliates. + * Copyright (c) 2017, 2021, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -32,6 +32,7 @@ */ package com.oracle.javafx.scenebuilder.app; +import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.app.util.MessageBox; import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform; import static com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform.IS_LINUX; @@ -61,18 +62,18 @@ public static synchronized String getApplicationDataFolder() { if (applicationDataFolder == null) { final String appName = "Scene Builder"; //NOI18N - + final String appVersion = getApplicationVersion(); if (IS_WINDOWS) { applicationDataFolder - = System.getenv("APPDATA") + "\\" + appName; //NOI18N + = System.getenv("APPDATA") + "\\" + appName + "-" + appVersion; //NOI18N } else if (IS_MAC) { applicationDataFolder = System.getProperty("user.home") //NOI18N + "/Library/Application Support/" //NOI18N - + appName; + + appName + "-" + appVersion; //NOI18N } else if (IS_LINUX) { applicationDataFolder - = System.getProperty("user.home") + "/.scenebuilder"; //NOI18N + = System.getProperty("user.home") + "/.scenebuilder-"+appVersion; //NOI18N } } @@ -80,7 +81,22 @@ public static synchronized String getApplicationDataFolder() { return applicationDataFolder; } - + + /* + * TODO: + * How to deal with snapshot versions? + * Shall the suffix "-SNAPSHOT" be ignored? + * + * For testing it would be helpful to have + * the snapshot suffix as with that one could run + * released vs. non-released SB versions. + */ + private static String getApplicationVersion() { + String version = AppSettings.getSceneBuilderVersion(); + assert version != null; + assert !version.isBlank(); + return version; + } public static synchronized String getUserLibraryFolder() { @@ -96,15 +112,15 @@ public static synchronized String getUserLibraryFolder() { * @return Directory path for Scene Builder logs */ public static synchronized String getLogFolder() { + final String appVersion = AppSettings.getSceneBuilderVersion(); if (logsFolder == null) { - logsFolder = Paths.get(System.getProperty("user.home"), ".scenebuilder", "logs").toString(); //NOI18N + logsFolder = Paths.get(System.getProperty("user.home"), ".scenebuilder-"+appVersion, "logs").toString(); //NOI18N } return logsFolder; } - public static boolean requestStart( - AppNotificationHandler notificationHandler, Application.Parameters parameters) - throws IOException { + public static boolean requestStart(AppNotificationHandler notificationHandler, + Application.Parameters parameters) throws IOException { if (EditorPlatform.isAssertionEnabled()) { // Development mode : we do not delegate to the existing instance notificationHandler.handleLaunch(parameters.getUnnamed()); @@ -126,9 +142,8 @@ public interface AppNotificationHandler { * Private (requestStartGeneric) */ - private static synchronized boolean requestStartGeneric( - AppNotificationHandler notificationHandler, Application.Parameters parameters) - throws IOException { + private static synchronized boolean requestStartGeneric(AppNotificationHandler notificationHandler, + Application.Parameters parameters) throws IOException { assert notificationHandler != null; assert parameters != null; assert messageBox == null; @@ -165,7 +180,7 @@ private static synchronized boolean requestStartGeneric( return result; } - private static String getMessageBoxFolder() { + protected static String getMessageBoxFolder() { if (messageBoxFolder == null) { messageBoxFolder = getApplicationDataFolder() + "/MB"; //NOI18N } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java index cf33a7ebf..e06a087d4 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java @@ -33,6 +33,7 @@ package com.oracle.javafx.scenebuilder.app.preferences; import com.oracle.javafx.scenebuilder.app.DocumentWindowController; +import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; import java.util.HashMap; @@ -54,7 +55,7 @@ public class PreferencesController extends PreferencesControllerBase{ **************************************************************************/ // PREFERENCES NODE NAME - static final String SB_RELEASE_NODE = "SB_2.0"; //NOI18N + static final String SB_RELEASE_NODE = "SB_"+AppSettings.getSceneBuilderVersion(); //NOI18N // GLOBAL PREFERENCES static final String TOOL_THEME = "TOOL_THEME"; //NOI18N @@ -180,4 +181,8 @@ public void clearRecentItems() { public PreferencesRecordGlobal getRecordGlobal() { return (PreferencesRecordGlobal) recordGlobal; } + + protected String getEffectiveUsedRootNode() { + return applicationRootPreferences.absolutePath(); + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java new file mode 100644 index 000000000..1f116bf5c --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; + +import org.junit.Test; + +import com.oracle.javafx.scenebuilder.app.util.AppSettings; + +public class AppPlatformTest { + + private final String appVersion = AppSettings.getSceneBuilderVersion(); + + @Test + public void that_windows_application_data_directory_is_specific_to_version() { + assumeTrue(getOsName().contains("windows")); + Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + Path expected = Paths.get(System.getenv("APPDATA")) + .resolve("Scene Builder-"+appVersion); + assertEquals(expected, appDataDir); + } + + @Test + public void that_mac_application_data_directory_is_specific_to_version() { + assumeTrue(getOsName().contains("mac")); + Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + Path expected = Paths.get(System.getProperty("user.home")) + .resolve("Library") + .resolve("Application Support") + .resolve("Scene Builder-"+appVersion); + assertEquals(expected, appDataDir); + } + + @Test + public void that_linux_application_data_directory_is_specific_to_version() { + assumeTrue(getOsName().contains("linux")); + Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + Path expected = Paths.get(System.getProperty("user.home")) + .resolve(".scenebuilder-"+appVersion); + assertEquals(expected, appDataDir); + } + + @Test + public void that_user_library_resides_in_application_settings_folder() { + Path userLibraryFolder = Paths.get(AppPlatform.getUserLibraryFolder()); + Path expected = Paths.get(AppPlatform.getApplicationDataFolder()) + .resolve("Library"); + assertEquals(expected, userLibraryFolder); + } + + @Test + public void that_logfile_stored_in_userhome_version_specific_log_dir() { + Path logDir = Paths.get(AppPlatform.getLogFolder()); + Path expected = Paths.get(System.getProperty("user.home")) + .resolve(".scenebuilder-"+appVersion) + .resolve("logs"); + assertEquals(expected, logDir); + } + + @Test + public void that_messagebox_is_placed_in_application_dir() { + Path mboxDir = Paths.get(AppPlatform.getMessageBoxFolder()); + Path expected = Paths.get(AppPlatform.getApplicationDataFolder()) + .resolve("MB"); + assertEquals(expected, mboxDir); + } + + private String getOsName() { + return System.getProperty("os.name").toLowerCase(Locale.ROOT); + } +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java new file mode 100644 index 000000000..042a45b3a --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2021, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.oracle.javafx.scenebuilder.app.util.AppSettings; + +public class PreferencesControllerTest { + + @Test + public void that_preferences_are_stored_per_version() { + String appVersion = AppSettings.getSceneBuilderVersion(); + String versionSpecificNode = "SB_"+appVersion; + assertEquals(versionSpecificNode, PreferencesController.SB_RELEASE_NODE); + } + + @Test + public void that_prefs_root_node_is_version_specific() { + String prefsNodeUsed = PreferencesController.getSingleton() + .getEffectiveUsedRootNode(); + String appVersion = AppSettings.getSceneBuilderVersion(); + String versionSpecificNode = "SB_"+appVersion; + String expectedPrefsNode = "/com/oracle/javafx/scenebuilder/app/preferences/"+versionSpecificNode; + assertEquals(expectedPrefsNode, prefsNodeUsed); + } +} From 34bd44f394e736a8a4d5bdac1db4b1efa82f0f0c Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 22 Dec 2021 20:48:59 +0100 Subject: [PATCH 02/42] Implemented a process to conditionally import previous SceneBuilder version Preferences. --- app/pom.xml | 7 + .../scenebuilder/app/SceneBuilderApp.java | 8 +- .../app/preferences/AppVersion.java | 159 ++++++++++++++ .../preferences/PreferencesController.java | 76 ++++++- .../app/preferences/PreferencesImporter.java | 205 ++++++++++++++++++ .../app/preferences/VersionedPreferences.java | 42 ++++ .../VersionedPreferencesFinder.java | 131 +++++++++++ .../app/preferences/AppVersionTest.java | 159 ++++++++++++++ .../PreferencesControllerTest.java | 105 ++++++++- .../preferences/PreferencesImporterTest.java | 108 +++++++++ .../app/preferences/PrefsHelper.java | 73 +++++++ .../VersionedPreferencesFinderTest.java | 106 +++++++++ docs/snippets/AppConfiguration.md | 57 +++++ .../scenebuilder/kit/alert/SBAlert.java | 11 +- .../PreferencesControllerBase.java | 55 +++-- .../PreferencesRecordArtifact.java | 6 +- .../PreferencesRecordRepository.java | 8 +- pom.xml | 3 +- 18 files changed, 1271 insertions(+), 48 deletions(-) create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferences.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java create mode 100644 docs/snippets/AppConfiguration.md diff --git a/app/pom.xml b/app/pom.xml index 5767a0843..4837766a4 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -25,6 +25,13 @@ kit 17.0.0 + + + junit + junit + 4.13.2 + test + diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index e522c8276..e549d8739 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -42,6 +42,7 @@ import com.oracle.javafx.scenebuilder.app.i18n.I18N; import com.oracle.javafx.scenebuilder.app.menubar.MenuBarController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; +import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesWindowController; import com.oracle.javafx.scenebuilder.app.registration.RegistrationWindowController; @@ -388,6 +389,9 @@ public void handleLaunch(List files) { setApplicationUncaughtExceptionHandler(); + PreferencesImporter prefsImporter = PreferencesController.getSingleton().getImporter(); + prefsImporter.askForActionAndRun(); + MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); // Creates the user library userLibrary = new UserLibrary(AppPlatform.getUserLibraryFolder(), @@ -792,9 +796,7 @@ private void performExit() { } } - private enum ACTION {START, STOP} - - ; + private enum ACTION {START, STOP}; private void logTimestamp(ACTION type) { switch (type) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java new file mode 100644 index 000000000..4ffbb0ffc --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import java.util.Comparator; +import java.util.Optional; + +/** + * A record type to work with Scene Builder versions following the semantic + * versioning schema. Patch versions can be null and will be ignored then. + * + * Version numbers can be sorted, where major beats minor beats patch versions. + * Version numbers with patch versions will be considered as higher (or newer) than version numbers without patch numbers. + * This is regardless if there is no patch number or patch version is 0. Patch version 0 is higher than no patch version at all. + */ +public record AppVersion(int major, int minor, Integer patch) implements Comparable { + + /** + * @return {@link Comparator} sorting {@link AppVersion} instances in descending order. + */ + public static Comparator descending() { + return (a,b)->b.compareTo(a); + } + + public AppVersion(int major, int minor) { + this(major, minor, null); + } + + @Override + public int compareTo(AppVersion o) { + int majorDiff = major - o.major; + int minorDiff = minor - o.minor; + int patchDiff = calcPatchDiff(o); + if (majorDiff == 0) { + if (minorDiff == 0) { + return patchDiff; + } + return minorDiff; + } + return majorDiff; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(major); + builder.append("."); + builder.append(minor); + if (patch != null) { + builder.append("."); + builder.append(patch); + } + return builder.toString(); + } + + /** + * Creates a version specific Scene Builder Preferences node name using the given prefix. + * @param prefix {@link String} used as prefix in Preferences node naming. + * @return + */ + public String nodeNameWithPrefix(String prefix) { + StringBuilder builder = new StringBuilder(); + if (prefix != null && !prefix.isBlank()) { + builder.append(prefix); + } + builder.append(major); + builder.append("."); + builder.append(minor); + if (patch != null) { + builder.append("."); + builder.append(patch); + } + return builder.toString(); + } + + protected int calcPatchDiff(AppVersion o) { + if (patch == null && o.patch == null) { + return 0; + } else if (patch == null && o.patch != null) { + return -1; + } else if (patch != null && o.patch == null) { + return 1; + } + return patch - o.patch; + } + + + /** + * Parses an optional AppVersion from any given String. + * Snapshot versions are not considered as valid. + * + * @param validVersion String representing a Scene Builder version. + * @return Empty optional when the String does not represent a valid version. If valid, then an optional AppVersion is returned. + */ + public static Optional fromString(String validVersion) { + String[] elements = validVersion.strip().split("[.]"); + return switch (elements.length) { + case 2 -> parseMajorMinor(elements); + case 3 -> parseMajorMinorPatch(elements); + default -> Optional.empty(); + }; + } + + private static Optional parseMajorMinor(String[] elements) { + try { + int major = Integer.parseInt(elements[0]); + int minor = Integer.parseInt(elements[1]); + if (major < 0 || minor < 0) { + return Optional.empty(); + } + return Optional.of(new AppVersion(major, minor)); + } catch (NumberFormatException nfe) { + return Optional.empty(); + } + } + + private static Optional parseMajorMinorPatch(String[] elements) { + try { + int major = Integer.parseInt(elements[0]); + int minor = Integer.parseInt(elements[1]); + int patch = Integer.parseInt(elements[2]); + if (major < 0 || minor < 0 || patch < 0) { + return Optional.empty(); + } + return Optional.of(new AppVersion(major, minor, patch)); + } catch (NumberFormatException nfe) { + return Optional.empty(); + } + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java index e06a087d4..d464fb1e9 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017 Gluon and/or its affiliates. + * Copyright (c) 2016, 2012 Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -32,17 +32,20 @@ */ package com.oracle.javafx.scenebuilder.app.preferences; -import com.oracle.javafx.scenebuilder.app.DocumentWindowController; -import com.oracle.javafx.scenebuilder.app.util.AppSettings; -import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; - import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; +import com.oracle.javafx.scenebuilder.app.DocumentWindowController; +import com.oracle.javafx.scenebuilder.app.util.AppSettings; +import com.oracle.javafx.scenebuilder.kit.preferences.MavenPreferences; +import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; +import com.oracle.javafx.scenebuilder.kit.preferences.RepositoryPreferences; + /** * Defines preferences for Scene Builder App. */ @@ -55,7 +58,8 @@ public class PreferencesController extends PreferencesControllerBase{ **************************************************************************/ // PREFERENCES NODE NAME - static final String SB_RELEASE_NODE = "SB_"+AppSettings.getSceneBuilderVersion(); //NOI18N + static final String SB_RELEASE_NODE_PREFIX = "SB_"; + static final String SB_RELEASE_NODE = SB_RELEASE_NODE_PREFIX+AppSettings.getSceneBuilderVersion(); //NOI18N // GLOBAL PREFERENCES static final String TOOL_THEME = "TOOL_THEME"; //NOI18N @@ -103,8 +107,8 @@ public class PreferencesController extends PreferencesControllerBase{ * * **************************************************************************/ - private PreferencesController() { - super(SB_RELEASE_NODE, new PreferencesRecordGlobal()); + private PreferencesController(Preferences rootNode) { + super(rootNode, SB_RELEASE_NODE, new PreferencesRecordGlobal()); // Cleanup document preferences at start time : final String items = applicationRootPreferences.get(RECENT_ITEMS, null); //NOI18N @@ -139,14 +143,31 @@ private PreferencesController() { **************************************************************************/ public static synchronized PreferencesController getSingleton() { + return getSingleton(null); + } + + /** + * This method allows to pass in a custom {@link Preferences} node, e.g. for testing. + * When configured with null, the Preferences node to be used will be defined internally. + * The custom node is only set with the first call, when the {@link PreferencesController} was initialized before, t + * the Preferences node to be used cannot be changed anymore. + * + * @param prefs {@link Preferences} node to be used, can be null. + * @return The one and only PreferencesController instance. + */ + protected static synchronized PreferencesController getSingleton(Preferences prefs) { if (singleton == null) { - singleton = new PreferencesController(); + singleton = new PreferencesController(prefs); singleton.getRecordGlobal().readFromJavaPreferences(); + } else { + if (prefs != null) { + Logger.getLogger(PreferencesController.class.getName()) + .log(Level.INFO, "PreferencesController was already initialized. Ignoring the provided preferences node."); + } } return singleton; } - public PreferencesRecordDocument getRecordDocument(final DocumentWindowController dwc) { final PreferencesRecordDocument recordDocument; if (recordDocuments.containsKey(dwc)) { @@ -185,4 +206,39 @@ public PreferencesRecordGlobal getRecordGlobal() { protected String getEffectiveUsedRootNode() { return applicationRootPreferences.absolutePath(); } + + /** + * If there were older versions of Scene Builder used, + * this will return the Preferences node matching the most recent previous version. + * @return Optional version number of + */ + protected Optional getPreviousVersionSettings() { + return new VersionedPreferencesFinder(SB_RELEASE_NODE_PREFIX, applicationPreferences) + .previousVersionPrefs(); + } + + /** + * This allows to re-initialize application settings after user has decided to + * import previous version settings. If not called after import, a restart of + * Scene Builder is needed to have all imported settings effective. + *

+ * Reloads the contents of {@link PreferencesRecordGlobal} and initializes + * {@link MavenPreferences} and {@link RepositoryPreferences} afterwards. + */ + protected void reload() { + getRecordGlobal().readFromJavaPreferences(); + initializeMavenPreferences(); + initializeRepositoryPreferences(); + } + + /** + * @return A {@link PreferencesImporter} instance for this application which + * will help to import previous version application settings. + */ + public PreferencesImporter getImporter() { + Optional previousVersionSettings = getPreviousVersionSettings(); + PreferencesImporter importer = new PreferencesImporter(applicationRootPreferences, previousVersionSettings); + importer.runAfterImport(this::reload); + return importer; + } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java new file mode 100644 index 000000000..84a98e294 --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import com.oracle.javafx.scenebuilder.kit.alert.SBAlert; + +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.ButtonType; + +/** + * Imports all keys and children (including keys) from an arbitrary + * {@link Preferences} node into the predefined applicationPreferences node. + */ +public class PreferencesImporter { + + /*************************************************************************** + * * + * Static fields * + * * + **************************************************************************/ + + // GLOBAL PREFERENCES + static final String ASKED_FOR_IMPORT = "ASKED_FOR_IMPORT"; + + /*************************************************************************** + * * + * Instance fields * + * * + **************************************************************************/ + + private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); + private final Preferences target; + private final Optional optionalSourceNode; + private Runnable actionAfterImport; + /** + * Creates a new Preferences importer. + * + * @param applicationPreferences Scene Builder {@link Preferences} node, + * determines where all settings shall be imported + * into. + * @param optionalSourceNode {@link VersionedPreferences} as a possible candidate to import settings from + */ + public PreferencesImporter(Preferences applicationPreferences, Optional optionalSourceNode) { + this.target = Objects.requireNonNull(applicationPreferences); + this.optionalSourceNode = Objects.requireNonNull(optionalSourceNode); + this.actionAfterImport = ()->logger.log(Level.INFO, "Importing settings completed. No post-import action defined."); + } + + /** + * Imports preferences from an existing node of an older Scene Builder version + * to the corresponding node of the current Scene Builder version. + * + * @param oldVersion {@link AppVersion} Version number so that, if needed, + * the import process can be customized depending on the + * version number. + * @param importSourceNode {@link Preferences} node of the previous version of + * Scene Builder. + * + * @throws BackingStoreException in case of problems accessing the Preferences + * store. + */ + protected void importFrom(Preferences importSourceNode) throws BackingStoreException { + copyChildren(importSourceNode,target); + } + + protected void importFrom(VersionedPreferences importSourceNode) throws BackingStoreException { + importFrom(importSourceNode.node()); + } + + + private void copyChildren(Preferences source, Preferences targetNode) throws BackingStoreException { + logger.log(Level.INFO, String.format("from node %s", source.name())); + String[] keys = source.keys(); + for (String key : keys) { + String value = source.get(key, null); + if (value != null) { + targetNode.put(key, value); + } + } + + String[] children = source.childrenNames(); + for (String child : children) { + copyChildren(source.node(child), targetNode.node(child)); + } + } + + /** + * Decides if the user shall be asked to import settings of previous Scene + * Builder version. Once the user made a decision, the preferences key + * ASKED_FOR_IMPORT is set, then this method will return false. + * + * @return true in case the import decision was not yet made. + */ + public boolean askForImport() { + String lastTimeAsked = target.get(ASKED_FOR_IMPORT, null); + return lastTimeAsked == null; + } + + /** + * Stores the preferences key ASKED_FOR_IMPORT upon request. If this key does + * not exist, Scene Builder will ask user to import settings from previous + * version during application startup. + */ + public void saveTimestampWhenAskedForImport() { + target.put(ASKED_FOR_IMPORT, LocalDateTime.now().toString()); + } + + /** + * Attempts to import settings of a previous version if existing. + * There is no user feedback in case of error. + */ + public void tryImportingPreviousVersionSettings() { + if (this.optionalSourceNode.isPresent()) { + VersionedPreferences source = this.optionalSourceNode.get(); + try { + importFrom(source); + } catch (Exception importError) { + logger.log(Level.SEVERE, String.format("Error during preferences import!", importError)); + } + try { + this.actionAfterImport.run(); + } catch (Exception postImportActionError) { + logger.log(Level.SEVERE, String.format("Error while running post-import action!", postImportActionError)); + } + saveTimestampWhenAskedForImport(); + } + } + + /** + * Decides if the user shall be asked to import settings of previous Scene + * Builder version but ONLY when settings of a previous version have been found. + * + * @return true when previous version settings have been found and user has not yet decided + */ + public boolean askForImportIfOlderSettingsExist() { + return this.optionalSourceNode.isPresent() && askForImport(); + } + + /** + * Defines an activity which will be executed after import of settings. + * + * @param action {@link Runnable} + * @throws NullPointerException when action is null + */ + public void runAfterImport(Runnable action) { + this.actionAfterImport = Objects.requireNonNull(action); + } + + /** + * Will raise a JavaFX {@link Alert} to ask the user whether to import previous version settings or not. + * The question will only appear in cases where previous version settings exist and the user decision has not been saved yet. + */ + public void askForActionAndRun() { + if (askForImportIfOlderSettingsExist()) { + SBAlert customAlert = new SBAlert(AlertType.CONFIRMATION, ButtonType.YES, ButtonType.NO); + customAlert.initOwner(null); + customAlert.setTitle("Gluon Scene Builder"); + customAlert.setHeaderText("Import settings"); + customAlert.setContentText("Previous version settings found.\nDo you want to import those?\n\nScene Builder will remember your decision and not ask again."); + Optional response = customAlert.showAndWait(); + System.out.println(response); + if (response.isPresent() && ButtonType.YES.equals(response.get())) { + tryImportingPreviousVersionSettings(); + } + } + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferences.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferences.java new file mode 100644 index 000000000..69c82bb3d --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferences.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import java.util.prefs.Preferences; + +/** + * Assigns an {@link AppVersion} to a given Preferences node. + * This will help with backward compatibility when structure of SB Preferences changes. + */ +public record VersionedPreferences(AppVersion version, Preferences node) { + +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java new file mode 100644 index 000000000..646cdb263 --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2021, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import com.oracle.javafx.scenebuilder.app.util.AppSettings; + +/** + * A utility which searches for version specific {@link Preferences} nodes belonging to Scene Builder. + * + */ +final class VersionedPreferencesFinder { + + private final Preferences applicationPreferences; + + private final String nodePrefix; + + public VersionedPreferencesFinder(String nodePrefix, Preferences rootNode) { + this.applicationPreferences = Objects.requireNonNull(rootNode); + this.nodePrefix = Objects.requireNonNull(nodePrefix); + } + + /** + * Collects the versions where application settings for Scene Builder exists into a list, starting with the most recent version. + * In case of errors while accessing the Preferences store, an empty list is returned. + * @return List with {@link AppVersion} starting with most recent version as first element. + */ + public List getDetectedVersions() { + try { + String[] children = applicationPreferences.childrenNames(); + return Arrays.stream(children) + .map(this::removePrefixFromVersionedNode) + .map(AppVersion::fromString) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(AppVersion.descending()) + .toList(); + } catch (BackingStoreException ex) { + Logger.getLogger(PreferencesController.class.getName()).log(Level.SEVERE, + "Failed to detect possibly existing settings of other versions!", ex); + } + return Collections.emptyList(); + } + + private String removePrefixFromVersionedNode(String nodeName) { + if (nodeName.startsWith(nodePrefix)) { + return nodeName.substring(nodePrefix.length()); + } + return nodeName; + } + + /** + * Provides the list of all preferences nodes with a version assigned to. + * Will not work with snapshot versions. + * + * @return List of preferences nodes (as {@link VersionedPreferences}) belonging to an older version of Scene Builder + */ + public List getPreviousVersions() { + var currentVersion = AppVersion.fromString(AppSettings.getSceneBuilderVersion()); + if (currentVersion.isPresent()) { + var current = currentVersion.get(); + return getDetectedVersions().stream() + .filter(other->other.compareTo(current) < 0) + .sorted(AppVersion.descending()) + .map(this::buildNode) + .toList(); + } + return Collections.emptyList(); + } + + /** + * If an older version of Scene Builder was used, there might be a preferences + * node for this older version. If this node exists, the optional will carry a + * {@link VersionedPreferences} providing the {@link AppVersion} and the + * {@link Preferences} node. + * + * @return Optional a {@link VersionedPreferences} in case an older version of Scene + * Builder was installed. When there are no settings, the optional will + * be empty. + */ + public Optional previousVersionPrefs() { + List previousVersions = getPreviousVersions(); + if (previousVersions.isEmpty()) { + return Optional.empty(); + } + return Optional.of(previousVersions.get(0)); + } + + private VersionedPreferences buildNode(AppVersion version) { + String node = version.nodeNameWithPrefix(nodePrefix); + return new VersionedPreferences(version, applicationPreferences.node(node)); + }; +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java new file mode 100644 index 000000000..7992a9266 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import static org.junit.Assert.*; + +import java.util.List; +import java.util.Optional; + +import org.junit.Test; + +public class AppVersionTest { + + @Test + public void that_comparision_yields_0_in_case_of_equal_versions() { + AppVersion old = new AppVersion(8, 5, 0); + AppVersion moreRecent = new AppVersion(8, 5, 0); + int result = moreRecent.compareTo(old); + assertTrue(result == 0); + } + + @Test + public void that_new_major_version_is_larger_than_older_major_version() { + AppVersion old = new AppVersion(8, 5, 0); + AppVersion moreRecent = new AppVersion(16, 0, 1); + int result = moreRecent.compareTo(old); + assertTrue(result > 0); + } + + @Test + public void that_minor_version_is_used_when_major_is_equal() { + AppVersion old = new AppVersion(16, 1, 0); + AppVersion moreRecent = new AppVersion(16, 2, 0); + int result = moreRecent.compareTo(old); + assertTrue(result > 0); + } + + @Test + public void that_patch_version_is_used_when_major_and_minor_are_equal() { + AppVersion old = new AppVersion(16, 0, 0); + AppVersion moreRecent = new AppVersion(16, 0, 1); + int result = moreRecent.compareTo(old); + assertTrue(result > 0); + } + + @Test + public void that_a_version_can_be_parsed_from_string() { + String validVersion = "42.12.12"; + AppVersion expected = new AppVersion(42, 12, 12); + Optional parsedVersion = AppVersion.fromString(validVersion); + assertTrue(parsedVersion.isPresent()); + assertEquals(expected, parsedVersion.get()); + } + + @Test + public void that_2digit_versions_are_parsed() { + assertTrue(AppVersion.fromString("2.0").isPresent()); + assertTrue(AppVersion.fromString("2.0.").isPresent()); + } + + @Test + public void that_useful_toString_exists() { + assertEquals("1.0", new AppVersion(1, 0).toString()); + assertEquals("1.2", new AppVersion(1, 2).toString()); + assertEquals("1.0.0", new AppVersion(1, 0, 0).toString()); + assertEquals("1.0.1", new AppVersion(1, 0, 1).toString()); + } + + @Test + public void that_empty_optionals_are_returned_from_illegal_version_strings() { + assertFalse(AppVersion.fromString("-1.-1.-1").isPresent()); + assertFalse(AppVersion.fromString("-1.10.10").isPresent()); + assertFalse(AppVersion.fromString("1.-10.-1").isPresent()); + assertFalse(AppVersion.fromString("1.1.-100").isPresent()); + assertFalse(AppVersion.fromString("-1.-1.-1").isPresent()); + assertFalse(AppVersion.fromString(".1.1.1.").isPresent()); + assertFalse(AppVersion.fromString("1..1").isPresent()); + assertFalse(AppVersion.fromString("11").isPresent()); + assertFalse(AppVersion.fromString("/t").isPresent()); + assertFalse(AppVersion.fromString(" . . ").isPresent()); + assertFalse(AppVersion.fromString("1.2.a").isPresent()); + assertFalse(AppVersion.fromString("-1.-2").isPresent()); + assertFalse(AppVersion.fromString("2.-1").isPresent()); + } + + @Test + public void that_node_name_is_created_with_prefix() { + assertEquals("SB_16.0.1", new AppVersion(16, 0, 1).nodeNameWithPrefix("SB_")); + assertEquals("TEST_16.0", new AppVersion(16, 0).nodeNameWithPrefix("TEST_")); + assertEquals("16.0", new AppVersion(16, 0).nodeNameWithPrefix("")); + assertEquals("16.0", new AppVersion(16, 0).nodeNameWithPrefix(null)); + } + + @Test + public void that_descending_sort_by_major_minor_patch_works() { + List versions = List.of( + new AppVersion(16, 0, 1), + new AppVersion(8, 5, 0), + new AppVersion(16, 0, 2), + new AppVersion(17, 0, 1), + new AppVersion(11, 2, 0), + new AppVersion(8, 5, 0), + new AppVersion(2, 0, 0), + new AppVersion(17, 0, 0), + new AppVersion(8, 5, 0), + new AppVersion(11, 1, 3), + new AppVersion(17, 2, 1), + new AppVersion(16, 0, 0), + new AppVersion(16, 0)); + + List sorted = versions.stream().sorted(AppVersion.descending()).toList(); + + List expectedOrder = List.of( + new AppVersion(17, 2, 1), + new AppVersion(17, 0, 1), + new AppVersion(17, 0, 0), + new AppVersion(16, 0, 2), + new AppVersion(16, 0, 1), + new AppVersion(16, 0, 0), + new AppVersion(16, 0), + new AppVersion(11, 2, 0), + new AppVersion(11, 1, 3), + new AppVersion(8, 5, 0), + new AppVersion(8, 5, 0), + new AppVersion(8, 5, 0), + new AppVersion(2, 0, 0)); + + assertEquals(expectedOrder, sorted); + } +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java index 042a45b3a..5ce6dd403 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Gluon and/or its affiliates. + * Copyright (c) 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -32,27 +32,120 @@ package com.oracle.javafx.scenebuilder.app.preferences; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import java.util.List; +import java.util.Optional; +import java.util.prefs.Preferences; + +import org.junit.BeforeClass; import org.junit.Test; import com.oracle.javafx.scenebuilder.app.util.AppSettings; +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.Repository; public class PreferencesControllerTest { + private static Preferences testNode; + + private static PreferencesController classUnderTest; + + @BeforeClass + public static void setup() { + testNode = Preferences.userRoot() + .node("SBTEST") + .node("com/oracle/javafx/scenebuilder/app/preferences"); + PrefsHelper.removeAllChildNodes(testNode); + classUnderTest = PreferencesController.getSingleton(testNode); + } + @Test public void that_preferences_are_stored_per_version() { String appVersion = AppSettings.getSceneBuilderVersion(); - String versionSpecificNode = "SB_"+appVersion; + String versionSpecificNode = "SB_" + appVersion; assertEquals(versionSpecificNode, PreferencesController.SB_RELEASE_NODE); } @Test public void that_prefs_root_node_is_version_specific() { - String prefsNodeUsed = PreferencesController.getSingleton() - .getEffectiveUsedRootNode(); + String prefsNodeUsed = classUnderTest.getEffectiveUsedRootNode(); String appVersion = AppSettings.getSceneBuilderVersion(); - String versionSpecificNode = "SB_"+appVersion; - String expectedPrefsNode = "/com/oracle/javafx/scenebuilder/app/preferences/"+versionSpecificNode; + String versionSpecificNode = "SB_" + appVersion; + String expectedPrefsNode = "/SBTEST/com/oracle/javafx/scenebuilder/app/preferences/" + versionSpecificNode; assertEquals(expectedPrefsNode, prefsNodeUsed); } + + @Test + public void that_most_recent_previous_version_is_detected() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_8.5").put("testkey", "testvalue"); + testNode.node("SB_2.0").put("testkey", "testvalue"); + Optional mostRecentPreviousVersion = classUnderTest.getPreviousVersionSettings(); + assertTrue(mostRecentPreviousVersion.isPresent()); + + assertEquals(new AppVersion(8, 5), mostRecentPreviousVersion.get().version()); + assertEquals("SB_8.5", mostRecentPreviousVersion.get().node().name()); + } + + @Test + public void that_no_version_is_detected_in_case_that_only_newer_versions_exist() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_199.199.199").put("testkey", "testvalue"); + Optional mostRecentPreviousVersion = classUnderTest.getPreviousVersionSettings(); + assertTrue(mostRecentPreviousVersion.isEmpty()); + } + + @Test + public void that_previous_version_settings_can_be_imported() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + + // GIVEN + + testNode.node("SB_8.9.9").put("RECENT_ITEMS", "/folder/file.fxml"); + + Preferences mavenLib = testNode.node("SB_8.9.9").node("ARTIFACTS").node("org.name:library:0.0.1"); + mavenLib.put("path", "/location/of/file.jar"); + mavenLib.put("groupID", "org.name"); + mavenLib.put("filter", ""); + mavenLib.put("dependencies", ""); + mavenLib.put("artifactId", "library"); + mavenLib.put("version", "0.0.1"); + + Preferences repository = testNode.node("SB_8.9.9").node("REPOSITORIES").node("custom-repository"); + repository.put("ID", "custom-repository"); + repository.put("URL", "http://localhost/myrepo"); + repository.put("type", "default"); + repository.put("User", "username"); + repository.put("Password", "password"); + + + // WHEN + PreferencesImporter importer = classUnderTest.getImporter(); + importer.tryImportingPreviousVersionSettings(); + + // THEN + String appVersion = AppSettings.getSceneBuilderVersion(); + String versionSpecificNode = "SB_" + appVersion; + Preferences targetNode = testNode.node(versionSpecificNode); + + // User Decision is memorized + assertNotNull(targetNode.get(PreferencesImporter.ASKED_FOR_IMPORT, null)); + + // Maven Repositories are initialized properly + List artifacts = classUnderTest.getMavenPreferences().getArtifactsCoordinates(); + assertFalse(artifacts.isEmpty()); + assertTrue(artifacts.contains("org.name:library:0.0.1")); + + // User Repositories are properly initialized + List repositories = classUnderTest.getRepositoryPreferences().getRepositories(); + assertFalse(repositories.isEmpty()); + assertEquals("http://localhost/myrepo", repositories.get(0).getURL()); + + // Recent items + List recentItems = classUnderTest.getRecordGlobal().getRecentItems(); + assertFalse(recentItems.isEmpty()); + assertEquals("/folder/file.fxml", recentItems.get(0)); + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java new file mode 100644 index 000000000..abd5cdf32 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -0,0 +1,108 @@ +package com.oracle.javafx.scenebuilder.app.preferences; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; +import java.util.prefs.Preferences; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +public class PreferencesImporterTest { + + private PreferencesImporter classUnderTest; + + @BeforeClass + @AfterClass + public static void cleanUpPrefs() throws Exception { + Set nodesToBeRemoved = Set.of("SOURCE_TO_IMPORT", + "SB_TEST_TARGET", + "SB_TEST_TARGET2", + "SB_OLD_VER"); + for (String nodeName : nodesToBeRemoved) { + Preferences.userRoot().node(nodeName).removeNode(); + } + } + @Test + public void that_settings_are_copied_between_nodes() throws Exception { + Preferences mySourcePrefs = Preferences.userRoot().node("SOURCE_TO_IMPORT"); + mySourcePrefs.put("anykey", "anyvalue"); + mySourcePrefs.node("CHILD1").put("key1", "value1"); + mySourcePrefs.node("CHILD1").node("CHILD2").put("key2", "value2"); + mySourcePrefs.node("CHILD1").node("CHILD2").node("CHILD3").put("key3", "value3"); + mySourcePrefs.node("CHILD1").node("CHILD2").node("CHILD4"); + + Preferences myTargetPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); + + classUnderTest = new PreferencesImporter(myTargetPrefs, Optional.empty()); + classUnderTest.importFrom(mySourcePrefs); + + assertEquals("anyvalue", myTargetPrefs.get("anykey", null)); + assertEquals("value1", myTargetPrefs.node("CHILD1").get("key1", null)); + assertEquals("value2", myTargetPrefs.node("CHILD1").node("CHILD2").get("key2", null)); + assertEquals("value3", myTargetPrefs.node("CHILD1").node("CHILD2").node("CHILD3").get("key3", null)); + } + + @Test + public void that_user_will_be_only_asked_when_import_decision_was_not_made() throws Exception { + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); + + assertTrue(classUnderTest.askForImport()); + + classUnderTest.saveTimestampWhenAskedForImport(); + assertFalse(classUnderTest.askForImport()); + } + + @Test + public void that_user_will_be_only_asked_when_previous_version_settings_exist() throws Exception { + AppVersion oldVersion = new AppVersion(0, 9); + Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); + + classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); + assertTrue(classUnderTest.askForImportIfOlderSettingsExist()); + + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); + assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); + + classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); + classUnderTest.saveTimestampWhenAskedForImport(); + assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); + } + + @Test + public void that_run_after_import_action_is_executed_in_tryImportingPreviousVersionSettings() { + Set responses = new HashSet<>(); + Runnable action = () -> responses.add("action performed"); + + AppVersion oldVersion = new AppVersion(0, 9); + Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); + olderPrefs.put("somekey", "1234"); + + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); + Optional previousVersionSettings = Optional.of(new VersionedPreferences(oldVersion, olderPrefs)); + + classUnderTest = new PreferencesImporter(appPrefs, previousVersionSettings); + classUnderTest.runAfterImport(action); + classUnderTest.tryImportingPreviousVersionSettings(); + + assertTrue(responses.contains("action performed")); + assertEquals("1234", appPrefs.get("somekey", null)); + assertNotNull(appPrefs.get(PreferencesImporter.ASKED_FOR_IMPORT, null)); + } + + @Test(expected = NullPointerException.class) + public void that_null_value_for_run_after_import_action_is_not_accepted() { + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); + classUnderTest.runAfterImport(null); + } + +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java new file mode 100644 index 000000000..594d633b6 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; + +import com.oracle.javafx.scenebuilder.app.util.AppSettings; + +/** + * Utility Class to help preparing Preferences nodes for tests + */ +class PrefsHelper { + private PrefsHelper() { + /* not intended for instantiation */ + } + public static void removeAllNonReleaseNodes(Preferences prefs) { + String appVersion = AppSettings.getSceneBuilderVersion(); + String versionSpecificNode = PreferencesController.SB_RELEASE_NODE_PREFIX + appVersion; + try { + String[] childnodes = prefs.childrenNames(); + for (String childName : childnodes) { + if (!childName.equals(versionSpecificNode)) { + prefs.node(childName).removeNode(); + prefs.flush(); + } + } + } catch (BackingStoreException e) { + throw new RuntimeException(e); + } + } + + public static void removeAllChildNodes(Preferences prefs) { + try { + String[] childnodes = prefs.childrenNames(); + for (String childName : childnodes) { + prefs.node(childName).removeNode(); + prefs.flush(); + } + } catch (BackingStoreException e) { + throw new RuntimeException(e); + } + } +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java new file mode 100644 index 000000000..d10713b30 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.preferences; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Optional; +import java.util.prefs.Preferences; + +import org.junit.BeforeClass; +import org.junit.Test; + +public class VersionedPreferencesFinderTest { + + private static Preferences testNode; + + private static VersionedPreferencesFinder classUnderTest; + + @BeforeClass + public static void setup() { + testNode = Preferences.userRoot() + .node("/SBTEST") + .node("/com/oracle/javafx/scenebuilder/app/preferences"); + PrefsHelper.removeAllChildNodes(testNode); + classUnderTest = new VersionedPreferencesFinder("SB_", testNode); + } + + @Test + public void that_other_existing_version_preferences_are_detected() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_2.0").put("testkey", "testvalue"); + testNode.node("SB_8.5").put("testkey", "testvalue"); + testNode.node("SB_16.1.2").put("testkey", "testvalue"); + List detectedVersions = classUnderTest.getDetectedVersions(); + assertTrue(detectedVersions.size() >= 3); + } + + @Test + public void that_an_empty_list_provided_when_no_other_prefs_versions_exist() throws Exception { + PrefsHelper.removeAllChildNodes(testNode); + List detectedVersions = classUnderTest.getDetectedVersions(); + assertTrue(detectedVersions.isEmpty()); + } + + @Test + public void that_previous_versions_are_properly_detected() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_8.5").put("testkey", "testvalue"); + testNode.node("SB_2.0").put("testkey", "testvalue"); + List previousVersionPrefs = classUnderTest.getPreviousVersions(); + List previousVersions = previousVersionPrefs.stream() + .map(VersionedPreferences::version) + .toList(); + assertEquals(List.of(new AppVersion(8,5),new AppVersion(2,0)), previousVersions); + } + + @Test + public void that_most_recent_previous_version_is_detected() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_8.5").put("testkey", "testvalue"); + testNode.node("SB_2.0").put("testkey", "testvalue"); + Optional mostRecentPreviousVersion = classUnderTest.previousVersionPrefs(); + assertTrue(mostRecentPreviousVersion.isPresent()); + assertEquals(new AppVersion(8, 5), mostRecentPreviousVersion.get().version()); + assertEquals("SB_8.5", mostRecentPreviousVersion.get().node().name()); + } + + @Test + public void that_no_version_is_detected_in_case_that_only_newer_versions_exist() { + PrefsHelper.removeAllNonReleaseNodes(testNode); + testNode.node("SB_199.199.199").put("testkey", "testvalue"); + Optional mostRecentPreviousVersion = classUnderTest.previousVersionPrefs(); + assertTrue(mostRecentPreviousVersion.isEmpty()); + } +} diff --git a/docs/snippets/AppConfiguration.md b/docs/snippets/AppConfiguration.md new file mode 100644 index 000000000..7dd7beb62 --- /dev/null +++ b/docs/snippets/AppConfiguration.md @@ -0,0 +1,57 @@ +# SceneBuilder Configuration + +## Data Storage + +| Location | Responsible Classes | Description | +| :---------------------- | :--------------------------------------------------------- | :---------- | +| applicationDataFolder | `c.o.j.scenebuilder.app.AppPlatform` | Message box and user library folders are located here | +| messageBoxFolder | `c.o.j.scenebuilder.app.AppPlatform` | Contents of message box | +| userLibraryFolder | `c.o.j.scenebuilder.app.AppPlatform` | May contain JAR file with a JavaFX controls inside | +| logsFolder | `c.o.j.scenebuilder.app.AppPlatform` | Here the `scenebuilder-x.y.z.log` file is stored, usually inside the users profiles directory | +| Java Preferences | `c.o.j.scenebuilder.app.preferences.PreferencesController` | Standardized persistent storage of application settings | + + +### ApplicationDataFolder + +| Platform | until Version 17 | Version 18 and later | +| -------- | -------------------- |---------------------------- | +| Windows | `%APPDATA%\Scene Builder` | `%APPDATA%\Scene Builder-18.0.1` | +| Linux | tbd. | tbd. | +| MacOS | tbd. | tbd. | + +### UserLibraryFolder + +| Location | +| -------------------------- | +| `applicationDataFolder/Library` | + +### MessageBoxFolder + +| Location | +| ---------------------- | +| `applicationDataFolder/MB` | + +### LogsFolder + +| Version | Location | +| ------- | ---------------------------------- | +| <= 17 | `%USERPROFILE%\.scenebuilder\logs` | +| >= 18 | `%USERPROFILE%\.scenebuilder-18.0.0\logs` | + +### Preferences + +Node Structure until version 17: + +Root Node: `com.oracle.javafx.scenebuilder.app.preferences` + * SB_2.0 (`IMPORTED_GLUON_JARS`, `LAST_SENT_TRACKING_INFO_DATE`, `RECENT_ITEMS`, `REGISTRATION_EMAIL`, `REGISTRATION_HASH`, `REGISTRATION_OPT_IN`) + * ARTIFACTS + * DOCUMENTS (separate node for each document's settings) + * REPOSITIRIES + +Structure with versions >= 18.0.0: + +Root Node: `com.oracle.javafx.scenebuilder.app.preferences` + * SB_18.0.0 + * ARTIFACTS + * DOCUMENTS + * REPOSITIRIES diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java index c38765b27..def6c49cc 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 Gluon and/or its affiliates. + * Copyright (c) 2017, 2022 Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -32,6 +32,7 @@ package com.oracle.javafx.scenebuilder.kit.alert; import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; import javafx.stage.Stage; /** @@ -39,6 +40,12 @@ */ public class SBAlert extends Alert { + public SBAlert(AlertType alertType, ButtonType... buttons) { + super(alertType, "", buttons); + getDialogPane().getStyleClass().add("SB-alert"); + getDialogPane().getStylesheets().add(SBAlert.class.getResource("Alert.css").toString()); + } + public SBAlert(AlertType alertType, Stage owner) { super(alertType); @@ -52,4 +59,6 @@ private void setIcons(Stage owner) { Stage alertStage = (Stage) getDialogPane().getScene().getWindow(); alertStage.getIcons().setAll(owner.getIcons()); } + + } diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesControllerBase.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesControllerBase.java index 9ad3e3a2f..db4450afb 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesControllerBase.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesControllerBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2019, Gluon and/or its affiliates. + * Copyright (c) 2017, 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -81,24 +81,31 @@ public abstract class PreferencesControllerBase { * * **************************************************************************/ + protected final Preferences applicationPreferences; protected final Preferences applicationRootPreferences; protected final PreferencesRecordGlobalBase recordGlobal; protected final Preferences documentsRootPreferences; - protected final Preferences artifactsRootPreferences; + protected final Preferences artifactsRootPreferences; protected final Preferences repositoriesRootPreferences; protected final MavenPreferences mavenPreferences; protected final RepositoryPreferences repositoryPreferences; - /*************************************************************************** * * * Constructors * * * **************************************************************************/ - public PreferencesControllerBase(String basePrefNodeName, PreferencesRecordGlobalBase recordGlobal) { - applicationRootPreferences = Preferences.userNodeForPackage(getClass()).node(basePrefNodeName); + public PreferencesControllerBase(Preferences rootNode, String basePrefNodeName, + PreferencesRecordGlobalBase recordGlobal) { + if (rootNode == null) { + applicationPreferences = Preferences.userNodeForPackage(getClass()); + applicationRootPreferences = applicationPreferences.node(basePrefNodeName); + } else { + applicationPreferences = rootNode; + applicationRootPreferences = applicationPreferences.node(basePrefNodeName); + } // Preferences global to the SB application this.recordGlobal = recordGlobal; @@ -120,26 +127,17 @@ public PreferencesControllerBase(String basePrefNodeName, PreferencesRecordGloba mavenPreferences = new MavenPreferences(); // create initial map of existing artifacts - try { - final String[] childrenNames = artifactsRootPreferences.childrenNames(); - for (String child : childrenNames) { - Preferences artifactPreferences = artifactsRootPreferences.node(child); - MavenArtifact mavenArtifact = new MavenArtifact(child); - mavenArtifact.setPath(artifactPreferences.get(PreferencesRecordArtifact.PATH, null)); - mavenArtifact.setDependencies(artifactPreferences.get(PreferencesRecordArtifact.DEPENDENCIES, null)); - mavenArtifact.setFilter(artifactPreferences.get(PreferencesRecordArtifact.FILTER, null)); - final PreferencesRecordArtifact recordArtifact = new PreferencesRecordArtifact( - artifactsRootPreferences, mavenArtifact); - mavenPreferences.addRecordArtifact(child, recordArtifact); - } - } catch (BackingStoreException ex) { - Logger.getLogger(PreferencesControllerBase.class.getName()).log(Level.SEVERE, null, ex); - } + initializeMavenPreferences(); // repositories repositoryPreferences = new RepositoryPreferences(); // create initial map of existing repositories + initializeRepositoryPreferences(); + + } + + public void initializeRepositoryPreferences() { try { final String[] childrenNames = repositoriesRootPreferences.childrenNames(); for (String child : childrenNames) { @@ -156,7 +154,24 @@ public PreferencesControllerBase(String basePrefNodeName, PreferencesRecordGloba } catch (BackingStoreException ex) { Logger.getLogger(PreferencesControllerBase.class.getName()).log(Level.SEVERE, null, ex); } + } + public void initializeMavenPreferences() { + try { + final String[] childrenNames = artifactsRootPreferences.childrenNames(); + for (String child : childrenNames) { + Preferences artifactPreferences = artifactsRootPreferences.node(child); + MavenArtifact mavenArtifact = new MavenArtifact(child); + mavenArtifact.setPath(artifactPreferences.get(PreferencesRecordArtifact.PATH, null)); + mavenArtifact.setDependencies(artifactPreferences.get(PreferencesRecordArtifact.DEPENDENCIES, null)); + mavenArtifact.setFilter(artifactPreferences.get(PreferencesRecordArtifact.FILTER, null)); + final PreferencesRecordArtifact recordArtifact = new PreferencesRecordArtifact( + artifactsRootPreferences, mavenArtifact); + mavenPreferences.addRecordArtifact(child, recordArtifact); + } + } catch (BackingStoreException ex) { + Logger.getLogger(PreferencesControllerBase.class.getName()).log(Level.SEVERE, null, ex); + } } /*************************************************************************** diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java index 6fd84a87e..5c31af9f1 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java @@ -44,9 +44,9 @@ public class PreferencesRecordArtifact { private final Preferences artifactsRootPreferences; private Preferences artifactPreferences; - private final static String GROUPID = "groupID"; - private final static String ARTIFACTID = "artifactId"; - private final static String VERSION = "version"; + private final static String GROUPID = "groupID"; + private final static String ARTIFACTID = "artifactId"; + private final static String VERSION = "version"; public final static String DEPENDENCIES = "dependencies"; public final static String FILTER = "filter"; public final static String PATH = "path"; diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java index 5c0dc241b..7ded2b299 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java @@ -44,11 +44,11 @@ public class PreferencesRecordRepository { private final Preferences repositoriesRootPreferences; private Preferences repositoryPreferences; - public final static String REPO_ID = "ID"; - public final static String REPO_TYPE = "type"; - public final static String REPO_URL = "URL"; + public final static String REPO_ID = "ID"; + public final static String REPO_TYPE = "type"; + public final static String REPO_URL = "URL"; public final static String REPO_USER = "User"; - public final static String REPO_PASS = "Password"; + public final static String REPO_PASS = "Password"; private final Repository repository; diff --git a/pom.xml b/pom.xml index 34d8e556f..e55f9430c 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 17 1.1.0 5.0.0-jdk9 - 11 + 17 UTF-8 @@ -56,6 +56,7 @@ 3.8.1 false + ${maven.compiler.release} From ae2be0e24daa97bbd732d58f6fc5efd627d732f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Wed, 5 Jan 2022 17:27:14 +0100 Subject: [PATCH 03/42] Replaced sysout with logging. --- .../scenebuilder/app/preferences/PreferencesImporter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 84a98e294..7d25c6ec3 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -196,8 +196,8 @@ public void askForActionAndRun() { customAlert.setHeaderText("Import settings"); customAlert.setContentText("Previous version settings found.\nDo you want to import those?\n\nScene Builder will remember your decision and not ask again."); Optional response = customAlert.showAndWait(); - System.out.println(response); if (response.isPresent() && ButtonType.YES.equals(response.get())) { + logger.log(Level.INFO, "User decided to import previous version settings."); tryImportingPreviousVersionSettings(); } } From 5c956a0cf90afe595311686695b83108c5a787c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Wed, 5 Jan 2022 21:42:14 +0100 Subject: [PATCH 04/42] Added a draft implementation of a UserLibraryImport process. --- .../javafx/scenebuilder/app/AppPlatform.java | 51 +-- .../scenebuilder/app/OperatingSystem.java | 59 ++++ .../scenebuilder/app/SceneBuilderApp.java | 8 +- .../app/library/user/AppDirectories.java | 63 ++++ .../app/library/user/UserLibraryImporter.java | 214 +++++++++++++ .../preferences/PreferencesController.java | 5 + .../app/preferences/PreferencesImporter.java | 15 +- .../library/user/UserLibraryImporterTest.java | 297 ++++++++++++++++++ .../PreferencesControllerTest.java | 13 +- .../preferences/PreferencesImporterTest.java | 2 +- .../app/preferences/PrefsHelper.java | 2 +- 11 files changed, 697 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 7e5d96d7f..c424af341 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -35,9 +35,6 @@ import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.app.util.MessageBox; import com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform; -import static com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform.IS_LINUX; -import static com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform.IS_MAC; -import static com.oracle.javafx.scenebuilder.kit.editor.EditorPlatform.IS_WINDOWS; import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; @@ -59,28 +56,40 @@ public class AppPlatform { private static MessageBox messageBox; public static synchronized String getApplicationDataFolder() { - + final String appVersion = getApplicationVersion(); if (applicationDataFolder == null) { - final String appName = "Scene Builder"; //NOI18N - final String appVersion = getApplicationVersion(); - if (IS_WINDOWS) { - applicationDataFolder - = System.getenv("APPDATA") + "\\" + appName + "-" + appVersion; //NOI18N - } else if (IS_MAC) { - applicationDataFolder - = System.getProperty("user.home") //NOI18N - + "/Library/Application Support/" //NOI18N - + appName + "-" + appVersion; //NOI18N - } else if (IS_LINUX) { - applicationDataFolder - = System.getProperty("user.home") + "/.scenebuilder-"+appVersion; //NOI18N - } + OperatingSystem os = OperatingSystem.get(); + final String appDataSubFolder = getApplicationDataSubFolder(os,appVersion); + assert appDataSubFolder != null; + + String appDataRoot = getApplicationDataRoot(os); + assert appDataRoot != null; + + applicationDataFolder = appDataRoot+appDataSubFolder; } - assert applicationDataFolder != null; - return applicationDataFolder; } + + public static String getApplicationDataRoot(OperatingSystem os) { + switch (os) { + case WINDOWS: return System.getenv("APPDATA") + "\\"; + case MACOS: return System.getProperty("user.home") + "/Library/Application Support/"; + case LINUX: return System.getProperty("user.home") + "/"; + default: return null; + } + } + public static String getApplicationDataSubFolder(OperatingSystem os, String version) { + final String appName = "Scene Builder"; //NOI18N + final String appVersion = (version == null) ? "" : version; + final String suffix = "".equalsIgnoreCase(appVersion) ? "" : "-"+appVersion; + switch (os) { + case WINDOWS: return appName+suffix; + case MACOS: return appName+suffix; + case LINUX: return ".scenebuilder"+suffix; + default: return null; + } + } /* * TODO: @@ -159,7 +168,7 @@ private static synchronized boolean requestStartGeneric(AppNotificationHandler n messageBox = new MessageBox<>(getMessageBoxFolder(), MessageBoxMessage.class, 1000 /* ms */); // Fix Start: Github Issue #301 final List parametersUnnamed = new ArrayList<>(parameters.getUnnamed()); - if (IS_MAC) { + if (OperatingSystem.get().equals(OperatingSystem.MACOS)) { parametersUnnamed.removeIf(p -> p.startsWith("-psn")); } // Fix End diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java new file mode 100644 index 000000000..a6a2bb11b --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app; + +import java.util.Locale; + +public enum OperatingSystem { + MACOS, + WINDOWS, + LINUX; + /** + * Obtains the operating system type from system property os.name. + * + * @return {@link OperatingSystem} + * @throws UnsupportedOperationException in case of an unknown operating system + */ + public static OperatingSystem get() { + String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); + if (osName.contains("linux")) { + return OperatingSystem.LINUX; + } + if (osName.contains("mac")) { + return OperatingSystem.MACOS; + } + if (osName.contains("windows")) { + return OperatingSystem.WINDOWS; + } + throw new UnsupportedOperationException("Unknown operating system platform!"); + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index e549d8739..1cf061428 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -40,6 +40,7 @@ import com.oracle.javafx.scenebuilder.kit.alert.ImportingGluonControlsAlert; import com.oracle.javafx.scenebuilder.kit.alert.SBAlert; import com.oracle.javafx.scenebuilder.app.i18n.I18N; +import com.oracle.javafx.scenebuilder.app.library.user.UserLibraryImporter; import com.oracle.javafx.scenebuilder.app.menubar.MenuBarController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; @@ -386,14 +387,15 @@ public void start(Stage stage) throws Exception { @Override public void handleLaunch(List files) { boolean showWelcomeDialog = files.isEmpty(); - setApplicationUncaughtExceptionHandler(); - PreferencesImporter prefsImporter = PreferencesController.getSingleton().getImporter(); prefsImporter.askForActionAndRun(); - MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); + UserLibraryImporter userLibImporter = PreferencesController.getSingleton().getUserLibraryImporter(); + userLibImporter.performImportWhenDesired(); + // Creates the user library + MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); userLibrary = new UserLibrary(AppPlatform.getUserLibraryFolder(), () -> mavenPreferences.getArtifactsPathsWithDependencies(), () -> mavenPreferences.getArtifactsFilter()); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java new file mode 100644 index 000000000..eeeaf7d76 --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.library.user; + +import com.oracle.javafx.scenebuilder.app.AppPlatform; +import com.oracle.javafx.scenebuilder.app.OperatingSystem; + +public interface AppDirectories { + String getApplicationDataRoot(OperatingSystem os); + + String getUserLibraryFolder(); + + String getApplicationDataSubFolder(OperatingSystem os, String string); + + public static AppDirectories usingAppPlatform() { + return new AppDirectories() { + @Override + public String getUserLibraryFolder() { + return AppPlatform.getUserLibraryFolder(); + } + + @Override + public String getApplicationDataSubFolder(OperatingSystem os, String version) { + return AppPlatform.getApplicationDataSubFolder(os, version); + } + + @Override + public String getApplicationDataRoot(OperatingSystem os) { + return AppPlatform.getApplicationDataRoot(os); + } + + }; + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java new file mode 100644 index 000000000..05da7c725 --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.library.user; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.prefs.Preferences; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.oracle.javafx.scenebuilder.app.OperatingSystem; +import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; +import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; +import com.oracle.javafx.scenebuilder.app.util.AppSettings; + +public class UserLibraryImporter { + protected static final String USER_LIBRARY_IMPORT_COMPLETE = "-userlib-import-done"; + + private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); + private final OperatingSystem os; + private final Optional version; + private final Preferences preferences; + private final AppDirectories appDirectories; + + public UserLibraryImporter(Preferences applicationPreferences) { + this(AppDirectories.usingAppPlatform(), applicationPreferences); + } + + public UserLibraryImporter(AppDirectories directories, Preferences applicationPreferences) { + this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), + OperatingSystem.get(), directories, applicationPreferences); + } + + public UserLibraryImporter(Optional appVersion, OperatingSystem os, AppDirectories appDirectories, Preferences applicationPreferences) { + this.os = Objects.requireNonNull(os); + this.version = Objects.requireNonNull(appVersion); + this.preferences = Objects.requireNonNull(applicationPreferences); + this.appDirectories = Objects.requireNonNull(appDirectories); + } + + /** + * The library import is only performed when following condition is met: + * a) the key {@code ASKED_FOR_IMPORT} does not exist in application preferences + * or b) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -no-import} + * or c) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -userlib-import-done} + * Of course, the import also takes only place if a previous version library folder is found. + */ + public void performImportWhenDesired() { + performImportWhenDesired(LocalDateTime.now()); + } + + protected void performImportWhenDesired(LocalDateTime timestamp) { + Objects.requireNonNull(timestamp); + String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); + if (importDecision.contains(PreferencesImporter.DECISION_NO_IMPORT)) { + logger.log(Level.INFO, "Previous version user library will not be imported as user opted out."); + return; + } + + if (importDecision.contains(USER_LIBRARY_IMPORT_COMPLETE)) { + logger.log(Level.INFO, "User library already imported."); + return; + } + importPreviousVersionUserLibrary(timestamp); + } + + protected void importPreviousVersionUserLibrary(LocalDateTime timestamp) { + String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()); + String importStatus = importDecision+USER_LIBRARY_IMPORT_COMPLETE; + preferences.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,importStatus); + String appDataDir = appDirectories.getApplicationDataRoot(os); + Path appData = Paths.get(appDataDir); + List candidates = collectLibraryCandidates(appData); + Optional oldSettingsDirectory = previousVersionUserLibraryPath(candidates); + if (oldSettingsDirectory.isPresent()) { + Path userLib = Paths.get(appDirectories.getUserLibraryFolder()); + importUserLibraryContentsFrom(oldSettingsDirectory.get(), userLib); + } + } + + private void importUserLibraryContentsFrom(Path sourceSettings, Path actualLibraryDir) { + Path oldLibrary = sourceSettings.resolve("Library"); + if (Files.exists(oldLibrary) && Files.isDirectory(oldLibrary)) { + logger.log(Level.INFO, oldLibrary.toAbsolutePath().toString()); + try { + copyDirectory(oldLibrary, actualLibraryDir); + } catch (IOException e) { + String template = "Failed to import user library from %s"; + String message = String.format(template, oldLibrary); + logger.log(Level.SEVERE, message, e); + } + } + } + + protected void copyDirectory(Path sourceDir, Path destinationDir) throws IOException { + Files.walk(sourceDir).forEach(item -> adjustDestinationPathAndTryCopy(sourceDir, destinationDir, item)); + } + + protected void adjustDestinationPathAndTryCopy(Path sourceDir, Path destinationDir, Path source) { + Path relativeSrc = sourceDir.relativize(source); + Path destination = destinationDir.resolve(relativeSrc).toAbsolutePath(); + tryFileCopy(source, destination); + } + + protected void tryFileCopy(Path source, Path destination) { + try { + createDirectoryIfNeeded(destination); + copyFile(source, destination); + } catch (IOException e) { + logger.log(Level.SEVERE, "Import failed", e); + } + } + + protected void copyFile(Path source, Path destination) throws IOException { + if (!Files.isDirectory(source)) { + logger.log(Level.INFO, "Importing {0} into {1}", new Object[] { source, destination }); + Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); + } + } + + protected void createDirectoryIfNeeded(Path destination) throws IOException { + if (Files.isDirectory(destination) && Files.notExists(destination)) { + Files.createDirectories(destination); + } + } + + private List collectLibraryCandidates(Path appData) { + try (Stream files = Files.list(appData)) { + return files.filter(Files::isDirectory).collect(Collectors.toList()); + } catch (IOException e) { + logger.log(Level.SEVERE, "Error while searching previous version user library locations.", e); + } + return null; + } + + protected Optional previousVersionUserLibraryPath(List candidates) { + Objects.requireNonNull(candidates, "list of path candidates must not be null"); + Map previousVersionLibDirs = collectPreviousVersionLibraryDirs(candidates); + return previousVersionLibDirs.entrySet().stream() + .sorted((a,b)->b.getKey().compareTo(a.getKey())) + .map(e->e.getValue()) + .findFirst(); + } + + protected Map collectPreviousVersionLibraryDirs(List candidates) { + if (version.isEmpty()) { + return Collections.emptyMap(); + } + AppVersion currentVersion = version.get(); + String legacyDirName = appDirectories.getApplicationDataSubFolder(os,""); + Map sceneBuilderDirs = new HashMap<>(); + for (Path candidate : candidates) { + String name = candidate.getFileName().toString(); + if (name.startsWith(legacyDirName)) { + if (name.contains("-")) { + String[] parts = name.split("[-]"); + if (parts.length == 2) { + AppVersion.fromString(parts[1]) + .ifPresent(v->{ + if (v.compareTo(currentVersion) < 0) { + sceneBuilderDirs.put(v, candidate); + } + }); + } + } else { + sceneBuilderDirs.put(new AppVersion(2, 0), candidate); + } + } + } + + return sceneBuilderDirs; + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java index d464fb1e9..4ad371abe 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java @@ -41,6 +41,7 @@ import java.util.prefs.Preferences; import com.oracle.javafx.scenebuilder.app.DocumentWindowController; +import com.oracle.javafx.scenebuilder.app.library.user.UserLibraryImporter; import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.preferences.MavenPreferences; import com.oracle.javafx.scenebuilder.kit.preferences.PreferencesControllerBase; @@ -241,4 +242,8 @@ public PreferencesImporter getImporter() { importer.runAfterImport(this::reload); return importer; } + + public UserLibraryImporter getUserLibraryImporter() { + return new UserLibraryImporter(applicationPreferences); + } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 7d25c6ec3..9d2548d35 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -58,7 +58,9 @@ public class PreferencesImporter { **************************************************************************/ // GLOBAL PREFERENCES - static final String ASKED_FOR_IMPORT = "ASKED_FOR_IMPORT"; + public static final String PREF_ASKED_FOR_IMPORT = "ASKED_FOR_IMPORT"; + + public static final String DECISION_NO_IMPORT = "-no-import"; /*************************************************************************** * * @@ -130,7 +132,7 @@ private void copyChildren(Preferences source, Preferences targetNode) throws Bac * @return true in case the import decision was not yet made. */ public boolean askForImport() { - String lastTimeAsked = target.get(ASKED_FOR_IMPORT, null); + String lastTimeAsked = target.get(PREF_ASKED_FOR_IMPORT, null); return lastTimeAsked == null; } @@ -138,9 +140,14 @@ public boolean askForImport() { * Stores the preferences key ASKED_FOR_IMPORT upon request. If this key does * not exist, Scene Builder will ask user to import settings from previous * version during application startup. + * */ public void saveTimestampWhenAskedForImport() { - target.put(ASKED_FOR_IMPORT, LocalDateTime.now().toString()); + target.put(PREF_ASKED_FOR_IMPORT, LocalDateTime.now().toString()); + } + + public void documentThatNoImportIsDesired() { + target.put(PREF_ASKED_FOR_IMPORT, LocalDateTime.now().toString()+DECISION_NO_IMPORT); } /** @@ -199,6 +206,8 @@ public void askForActionAndRun() { if (response.isPresent() && ButtonType.YES.equals(response.get())) { logger.log(Level.INFO, "User decided to import previous version settings."); tryImportingPreviousVersionSettings(); + } else { + documentThatNoImportIsDesired(); } } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java new file mode 100644 index 000000000..8ca85af53 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app.library.user; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.prefs.Preferences; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.oracle.javafx.scenebuilder.app.AppPlatform; +import com.oracle.javafx.scenebuilder.app.OperatingSystem; +import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; +import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; +import com.oracle.javafx.scenebuilder.app.preferences.PrefsHelper; + +public class UserLibraryImporterTest { + + private UserLibraryImporter classUnderTest; + + private static Preferences testNode; + + @BeforeClass + public static void setup() { + testNode = Preferences.userRoot() + .node("USR_LIB_IMPORTER_TEST") + .node("com/oracle/javafx/scenebuilder/app/preferences"); + PrefsHelper.removeAllChildNodes(testNode); + } + + @AfterClass + public static void cleanup() throws Exception { + Preferences.userRoot().node("USR_LIB_IMPORTER_TEST").removeNode(); + } + + @Test + public void that_exception_is_thrown_when_null_argument_is_used() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), OperatingSystem.LINUX, + AppDirectories.usingAppPlatform(),testNode); + assertThrows(NullPointerException.class, + ()->classUnderTest.previousVersionUserLibraryPath(null)); + } + @Test + public void that_empty_optional_is_provided_when_current_version_has_unsupported_format() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), OperatingSystem.LINUX, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of(Paths.get("Scene Builder"), + Paths.get("Scene Builder-19.0.1")); + assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); + } + + @Test + public void that_empty_optional_is_provided_when_old_library_path_not_exists() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of(Paths.get("Scene Builder"), + Paths.get("Scene Builder-19.0.1")); + assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); + } + @Test + public void that_user_library_paths_are_detected_for_LINUX() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get(".scenebuilder-15.0.0"), + Paths.get("Scene Builder"), + Paths.get(".scenebuilder-7.0-"), + Paths.get(".scenebuilder-8.5.0"), + Paths.get(".scenebuilder-8.5.0-SNAPSHOT"), + Paths.get("Scene Builder-19.0.1")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get(".scenebuilder-15.0.0"), x.get()); + } + + @Test + public void that_legacy_library_path_is_detected_for_LINUX() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get("Scene Builder"), + Paths.get("Scene Builder-19.0.1"), + Paths.get(".scenebuilder")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get(".scenebuilder"), x.get()); + } + + @Test + public void that_user_library_paths_are_detected_for_WINDOWS() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.WINDOWS, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get("Scene Builder-17.0.0"), + Paths.get("Scene Builder"), + Paths.get("Scene Builder-8.5.0"), + Paths.get("Scene Builder-19.0.1"), + Paths.get(".scenebuilder")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); + } + + @Test + public void that_legacy_library_path_is_detected_for_WINDOWS() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.WINDOWS, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get("Scene Builder"), + Paths.get("Scene Builder-19.0.1"), + Paths.get(".scenebuilder")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get("Scene Builder"), x.get()); + } + + @Test + public void that_user_library_paths_are_detected_for_MACOS() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.MACOS, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get("Scene Builder-17.0.0"), + Paths.get("Scene Builder"), + Paths.get("Scene Builder-8.5.0"), + Paths.get("Scene Builder-19.0.1"), + Paths.get(".scenebuilder")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); + } + + @Test + public void that_legacy_library_path_is_detected_for_MACOS() { + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.MACOS, + AppDirectories.usingAppPlatform(),testNode); + List candidates = List.of( + Paths.get("Scene Builder"), + Paths.get("Scene Builder-19.0.1"), + Paths.get(".scenebuilder")); + + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); + + assertFalse(x.isEmpty()); + assertEquals(Paths.get("Scene Builder"), x.get()); + } + + private AppDirectories creatLinuxLikeAppDirs(OperatingSystem os, String version) { + try { + Path tempDir = Files.createTempDirectory("SBUserLibImportTest_"); + return new AppDirectories() { + @Override + public String getUserLibraryFolder() { + return getApplicationDataRoot(os)+"/"+getApplicationDataSubFolder(os,version)+"/Library"; + } + @Override + public String getApplicationDataSubFolder(OperatingSystem os, String version) { + return AppPlatform.getApplicationDataSubFolder(os, version); + } + @Override + public String getApplicationDataRoot(OperatingSystem os) { + return tempDir.toString(); + } + }; + + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + @Test + public void that_import_is_skipped_when_user_opted_out() { + LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); + testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT, + timestamp.toString()+PreferencesImporter.DECISION_NO_IMPORT); + + String version = "17.0.0"; + OperatingSystem os = OperatingSystem.LINUX; + AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + classUnderTest.performImportWhenDesired(timestamp); + + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); + + assertNotNull(documentedResult); + assertEquals("2022-01-05T20:14-no-import", documentedResult); + assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); + } + + + @Test + public void that_import_is_skipped_when_done_before() { + LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); + testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,timestamp.toString()+UserLibraryImporter.USER_LIBRARY_IMPORT_COMPLETE); + + String version = "17.0.0"; + OperatingSystem os = OperatingSystem.LINUX; + AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + classUnderTest.performImportWhenDesired(timestamp); + + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); + + assertNotNull(documentedResult); + assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); + assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); + } + + @Test + public void that_old_files_are_imported() throws Exception { + LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); + testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,timestamp.toString()); + + String version = "17.0.0"; + OperatingSystem os = OperatingSystem.LINUX; + AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); + + + // Prepare an old library + String dataRoot = appDirectories.getApplicationDataRoot(OperatingSystem.LINUX); + Path oldLibrary = Paths.get(dataRoot).resolve(".scenebuilder").resolve("Library"); + Files.createDirectories(oldLibrary); + Path myJar = oldLibrary.resolve("MyCustomArtifact.txt"); + Files.writeString(myJar, "NotaJarButSomeTestContent", StandardOpenOption.CREATE); + + Path userLib = Paths.get(appDirectories.getUserLibraryFolder()); + Files.createDirectories(userLib); + + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + classUnderTest.performImportWhenDesired(timestamp); + + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); + + assertNotNull(documentedResult); + assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); + + File[] importedFiles = new File(appDirectories.getUserLibraryFolder()).listFiles(); + assertEquals(1, importedFiles.length); + assertEquals("MyCustomArtifact.txt", importedFiles[0].getName()); + } + +} diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java index 5ce6dd403..bd2fe5c0e 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java @@ -40,6 +40,7 @@ import java.util.Optional; import java.util.prefs.Preferences; +import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -55,11 +56,17 @@ public class PreferencesControllerTest { @BeforeClass public static void setup() { testNode = Preferences.userRoot() - .node("SBTEST") + .node("PREFS_CONTROLLER_TEST") .node("com/oracle/javafx/scenebuilder/app/preferences"); PrefsHelper.removeAllChildNodes(testNode); classUnderTest = PreferencesController.getSingleton(testNode); } + + @AfterClass + public static void cleanup() throws Exception { + Preferences.userRoot().node("PREFS_CONTROLLER_TEST").removeNode(); + } + @Test public void that_preferences_are_stored_per_version() { @@ -73,7 +80,7 @@ public void that_prefs_root_node_is_version_specific() { String prefsNodeUsed = classUnderTest.getEffectiveUsedRootNode(); String appVersion = AppSettings.getSceneBuilderVersion(); String versionSpecificNode = "SB_" + appVersion; - String expectedPrefsNode = "/SBTEST/com/oracle/javafx/scenebuilder/app/preferences/" + versionSpecificNode; + String expectedPrefsNode = "/PREFS_CONTROLLER_TEST/com/oracle/javafx/scenebuilder/app/preferences/" + versionSpecificNode; assertEquals(expectedPrefsNode, prefsNodeUsed); } @@ -131,7 +138,7 @@ public void that_previous_version_settings_can_be_imported() { Preferences targetNode = testNode.node(versionSpecificNode); // User Decision is memorized - assertNotNull(targetNode.get(PreferencesImporter.ASKED_FOR_IMPORT, null)); + assertNotNull(targetNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null)); // Maven Repositories are initialized properly List artifacts = classUnderTest.getMavenPreferences().getArtifactsCoordinates(); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index abd5cdf32..057708ced 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -95,7 +95,7 @@ public void that_run_after_import_action_is_executed_in_tryImportingPreviousVers assertTrue(responses.contains("action performed")); assertEquals("1234", appPrefs.get("somekey", null)); - assertNotNull(appPrefs.get(PreferencesImporter.ASKED_FOR_IMPORT, null)); + assertNotNull(appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null)); } @Test(expected = NullPointerException.class) diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java index 594d633b6..2a7c36a7c 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java @@ -39,7 +39,7 @@ /** * Utility Class to help preparing Preferences nodes for tests */ -class PrefsHelper { +public class PrefsHelper { private PrefsHelper() { /* not intended for instantiation */ } From b131c63acc102263a853a4260bbb94ce923dd5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Thu, 6 Jan 2022 00:04:58 +0100 Subject: [PATCH 05/42] Moved OS depending behavior in separate class. Also introduced an interface to access platform directories in order to make the OS specific behavior testable. --- .../javafx/scenebuilder/app/AppPlatform.java | 80 +++++--------- .../scenebuilder/app/PlatformDirectories.java | 85 ++++++++++++++ .../app/PlatformSpecificDirectories.java | 70 ++++++++++++ .../app/library/user/UserLibraryImporter.java | 26 +++-- .../app/preferences/PreferencesImporter.java | 28 +++-- .../app/library/user/TestAppDirectories.java} | 53 +++++---- .../library/user/UserLibraryImporterTest.java | 104 +++++++++--------- .../preferences/PreferencesImporterTest.java | 61 +++++++++- 8 files changed, 354 insertions(+), 153 deletions(-) create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java create mode 100644 app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java rename app/src/{main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java => test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java} (66%) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index c424af341..046535c61 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -48,49 +48,27 @@ * */ public class AppPlatform { - - private static String applicationDataFolder; - private static String userLibraryFolder; - private static String messageBoxFolder; - private static String logsFolder; private static MessageBox messageBox; + private static PlatformSpecificDirectories platformSpecificDirectories; public static synchronized String getApplicationDataFolder() { - final String appVersion = getApplicationVersion(); - if (applicationDataFolder == null) { - OperatingSystem os = OperatingSystem.get(); - final String appDataSubFolder = getApplicationDataSubFolder(os,appVersion); - assert appDataSubFolder != null; - - String appDataRoot = getApplicationDataRoot(os); - assert appDataRoot != null; - - applicationDataFolder = appDataRoot+appDataSubFolder; + if (platformSpecificDirectories == null) { + platformSpecificDirectories = getAppDirectories(); } - assert applicationDataFolder != null; - return applicationDataFolder; + return platformSpecificDirectories.getApplicationDataFolder(); } - public static String getApplicationDataRoot(OperatingSystem os) { - switch (os) { - case WINDOWS: return System.getenv("APPDATA") + "\\"; - case MACOS: return System.getProperty("user.home") + "/Library/Application Support/"; - case LINUX: return System.getProperty("user.home") + "/"; - default: return null; - } - } - public static String getApplicationDataSubFolder(OperatingSystem os, String version) { - final String appName = "Scene Builder"; //NOI18N - final String appVersion = (version == null) ? "" : version; - final String suffix = "".equalsIgnoreCase(appVersion) ? "" : "-"+appVersion; - switch (os) { - case WINDOWS: return appName+suffix; - case MACOS: return appName+suffix; - case LINUX: return ".scenebuilder"+suffix; - default: return null; + public static synchronized PlatformSpecificDirectories getAppDirectories() { + final String appVersion = getApplicationVersion(); + if (platformSpecificDirectories == null) { + OperatingSystem os = OperatingSystem.get(); + platformSpecificDirectories = new PlatformSpecificDirectories(os, appVersion); } + assert platformSpecificDirectories != null; + return platformSpecificDirectories; } - + + /* * TODO: * How to deal with snapshot versions? @@ -108,12 +86,10 @@ private static String getApplicationVersion() { } public static synchronized String getUserLibraryFolder() { - - if (userLibraryFolder == null) { - userLibraryFolder = getApplicationDataFolder() + "/Library"; //NOI18N + if (platformSpecificDirectories == null) { + platformSpecificDirectories = getAppDirectories(); } - - return userLibraryFolder; + return platformSpecificDirectories.getUserLibraryFolder(); } /** @@ -121,13 +97,19 @@ public static synchronized String getUserLibraryFolder() { * @return Directory path for Scene Builder logs */ public static synchronized String getLogFolder() { - final String appVersion = AppSettings.getSceneBuilderVersion(); - if (logsFolder == null) { - logsFolder = Paths.get(System.getProperty("user.home"), ".scenebuilder-"+appVersion, "logs").toString(); //NOI18N + if (platformSpecificDirectories == null) { + platformSpecificDirectories = getAppDirectories(); } - return logsFolder; + return platformSpecificDirectories.getLogFolder(); } - + + protected static String getMessageBoxFolder() { + if (platformSpecificDirectories == null) { + platformSpecificDirectories = getAppDirectories(); + } + return platformSpecificDirectories.getMessageBoxFolder(); + } + public static boolean requestStart(AppNotificationHandler notificationHandler, Application.Parameters parameters) throws IOException { if (EditorPlatform.isAssertionEnabled()) { @@ -189,14 +171,6 @@ private static synchronized boolean requestStartGeneric(AppNotificationHandler n return result; } - protected static String getMessageBoxFolder() { - if (messageBoxFolder == null) { - messageBoxFolder = getApplicationDataFolder() + "/MB"; //NOI18N - } - - return messageBoxFolder; - } - private static class MessageBoxMessage extends ArrayList { static final long serialVersionUID = 10; public MessageBoxMessage(List strings) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java new file mode 100644 index 000000000..5c65ca66c --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.app; + +public interface PlatformDirectories { + + /** + * @return the root location where application data shall be stored on the + * corresponding platform. + */ + String getApplicationDataRoot(); + + /** + * Scene Builder provides the option to hold custom JAR files in a user library. + * This method provides the full path to the location of the user library + * directory. Typically, the user library directory (Library) is placed inside + * the application data folder. + */ + String getUserLibraryFolder(); + + /** + * @return Provides the name of the by default version specific application data + * sub directory. This folder is will be located inside the application + * data root folder. + */ + String getApplicationDataSubFolder(); + + /** + * In previous versions, Scene Builder stored its files in a directory without + * version number. Hence, in some cases it might be helpful to control when the + * version number is used or not. + * + * @param includeVersion If true, the version number might be a part of the sub + * folder name. + * @return Provides the name of the application data sub directory with or + * without version information. + */ + String getApplicationDataSubFolder(boolean includeVersion); + + /** + * The application data folder is usually a child folder inside the application + * data root. + * + * @return the exact location, specifically for this version of Scene Builder, + * where settings and arbitrary files can be placed. + */ + String getApplicationDataFolder(); + + /** + * + * @return + */ + String getLogFolder(); + + String getMessageBoxFolder(); +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java new file mode 100644 index 000000000..4038c97bd --- /dev/null +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -0,0 +1,70 @@ +package com.oracle.javafx.scenebuilder.app; + +import java.nio.file.Paths; +import java.util.Objects; + +/** + * This is where all relevant application directories and paths are defined. + * If a new operating system (OS) platform shall be supported, the OS specific + * behavior is supposed to be implemented here in. + */ +public class PlatformSpecificDirectories implements PlatformDirectories { + protected final OperatingSystem os; + protected final String version; + + public PlatformSpecificDirectories(OperatingSystem os, String appVersion) { + this.os = Objects.requireNonNull(os); + this.version = appVersion; + } + + @Override + public String getApplicationDataFolder() { + String appDataRoot = getApplicationDataRoot(); + String appDataSubFolder = getApplicationDataSubFolder(); + return appDataRoot+"/"+appDataSubFolder; + } + + @Override + public String getApplicationDataRoot() { + switch (os) { + case WINDOWS: return System.getenv("APPDATA") + "\\"; + case MACOS: return System.getProperty("user.home") + "/Library/Application Support/"; + case LINUX: return System.getProperty("user.home") + "/"; + default: return null; + } + } + + @Override + public String getUserLibraryFolder() { + return getApplicationDataFolder() + "/Library"; + } + + @Override + public String getApplicationDataSubFolder() { + return getApplicationDataSubFolder(true); + } + + @Override + public String getApplicationDataSubFolder(boolean includeVersion) { + final String appName = "Scene Builder"; //NOI18N + final String appVersion = (version == null) ? "" : version; + final String suffix = ("".equalsIgnoreCase(appVersion) || !includeVersion) ? "" : "-"+appVersion; + return switch (os) { + case WINDOWS -> appName+suffix; + case MACOS -> appName+suffix; + case LINUX -> ".scenebuilder"+suffix; + default -> null; + }; + } + + @Override + public String getLogFolder() { + return Paths.get(System.getProperty("user.home"), + ".scenebuilder-"+version, "logs").toString(); + } + + @Override + public String getMessageBoxFolder() { + return getApplicationDataFolder() + "/MB"; + } +} diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index 05da7c725..3a849b078 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -49,7 +49,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.javafx.scenebuilder.app.OperatingSystem; +import com.oracle.javafx.scenebuilder.app.PlatformDirectories; +import com.oracle.javafx.scenebuilder.app.AppPlatform; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; import com.oracle.javafx.scenebuilder.app.util.AppSettings; @@ -58,26 +59,27 @@ public class UserLibraryImporter { protected static final String USER_LIBRARY_IMPORT_COMPLETE = "-userlib-import-done"; private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); - private final OperatingSystem os; private final Optional version; private final Preferences preferences; - private final AppDirectories appDirectories; + private final PlatformDirectories appDirectories; public UserLibraryImporter(Preferences applicationPreferences) { - this(AppDirectories.usingAppPlatform(), applicationPreferences); + this(AppPlatform.getAppDirectories(), applicationPreferences); } - public UserLibraryImporter(AppDirectories directories, Preferences applicationPreferences) { - this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), - OperatingSystem.get(), directories, applicationPreferences); + public UserLibraryImporter(PlatformDirectories directories, Preferences applicationPreferences) { + this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), directories, applicationPreferences); } - public UserLibraryImporter(Optional appVersion, OperatingSystem os, AppDirectories appDirectories, Preferences applicationPreferences) { - this.os = Objects.requireNonNull(os); + public UserLibraryImporter(Optional appVersion, PlatformDirectories appDirectories, Preferences applicationPreferences) { this.version = Objects.requireNonNull(appVersion); this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); } + + protected PlatformDirectories getPlatformDirectories() { + return this.appDirectories; + } /** * The library import is only performed when following condition is met: @@ -109,7 +111,7 @@ protected void importPreviousVersionUserLibrary(LocalDateTime timestamp) { String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()); String importStatus = importDecision+USER_LIBRARY_IMPORT_COMPLETE; preferences.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,importStatus); - String appDataDir = appDirectories.getApplicationDataRoot(os); + String appDataDir = appDirectories.getApplicationDataRoot(); Path appData = Paths.get(appDataDir); List candidates = collectLibraryCandidates(appData); Optional oldSettingsDirectory = previousVersionUserLibraryPath(candidates); @@ -171,7 +173,7 @@ private List collectLibraryCandidates(Path appData) { } catch (IOException e) { logger.log(Level.SEVERE, "Error while searching previous version user library locations.", e); } - return null; + return Collections.emptyList(); } protected Optional previousVersionUserLibraryPath(List candidates) { @@ -188,7 +190,7 @@ protected Map collectPreviousVersionLibraryDirs(List can return Collections.emptyMap(); } AppVersion currentVersion = version.get(); - String legacyDirName = appDirectories.getApplicationDataSubFolder(os,""); + String legacyDirName = appDirectories.getApplicationDataSubFolder(false); Map sceneBuilderDirs = new HashMap<>(); for (Path candidate : candidates) { String name = candidate.getFileName().toString(); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 9d2548d35..999d7dda5 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -34,6 +34,7 @@ import java.time.LocalDateTime; import java.util.Objects; import java.util.Optional; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.BackingStoreException; @@ -196,19 +197,30 @@ public void runAfterImport(Runnable action) { * The question will only appear in cases where previous version settings exist and the user decision has not been saved yet. */ public void askForActionAndRun() { - if (askForImportIfOlderSettingsExist()) { + Supplier> alertInteraction = ()->{ SBAlert customAlert = new SBAlert(AlertType.CONFIRMATION, ButtonType.YES, ButtonType.NO); customAlert.initOwner(null); customAlert.setTitle("Gluon Scene Builder"); customAlert.setHeaderText("Import settings"); customAlert.setContentText("Previous version settings found.\nDo you want to import those?\n\nScene Builder will remember your decision and not ask again."); - Optional response = customAlert.showAndWait(); - if (response.isPresent() && ButtonType.YES.equals(response.get())) { - logger.log(Level.INFO, "User decided to import previous version settings."); - tryImportingPreviousVersionSettings(); - } else { - documentThatNoImportIsDesired(); - } + return customAlert.showAndWait(); + }; + askForActionAndRun(alertInteraction); + } + + protected void askForActionAndRun(Supplier> alertInteraction) { + if (askForImportIfOlderSettingsExist()) { + executeInteractionAndImport(alertInteraction); + } + } + + protected void executeInteractionAndImport(Supplier> alertInteraction) { + Optional response = alertInteraction.get(); + if (response.isPresent() && ButtonType.YES.equals(response.get())) { + logger.log(Level.INFO, "User decided to import previous version settings."); + tryImportingPreviousVersionSettings(); + } else { + documentThatNoImportIsDesired(); } } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java similarity index 66% rename from app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java rename to app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java index eeeaf7d76..2cce0ca87 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/AppDirectories.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java @@ -31,33 +31,38 @@ */ package com.oracle.javafx.scenebuilder.app.library.user; -import com.oracle.javafx.scenebuilder.app.AppPlatform; -import com.oracle.javafx.scenebuilder.app.OperatingSystem; - -public interface AppDirectories { - String getApplicationDataRoot(OperatingSystem os); +import java.io.IOException; +import java.io.UncheckedIOException; +import java.nio.file.Files; +import java.nio.file.Path; - String getUserLibraryFolder(); +import com.oracle.javafx.scenebuilder.app.OperatingSystem; +import com.oracle.javafx.scenebuilder.app.PlatformSpecificDirectories; - String getApplicationDataSubFolder(OperatingSystem os, String string); +public class TestAppDirectories extends PlatformSpecificDirectories { - public static AppDirectories usingAppPlatform() { - return new AppDirectories() { - @Override - public String getUserLibraryFolder() { - return AppPlatform.getUserLibraryFolder(); - } - - @Override - public String getApplicationDataSubFolder(OperatingSystem os, String version) { - return AppPlatform.getApplicationDataSubFolder(os, version); - } - - @Override - public String getApplicationDataRoot(OperatingSystem os) { - return AppPlatform.getApplicationDataRoot(os); - } + private final Path tempDir = createDirectory(); + public TestAppDirectories(OperatingSystem os, String appVersion) { + super(os, appVersion); + } + + private Path createDirectory() { + try { + return Files.createTempDirectory("SBTEST_APPDATA_"); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } - }; + @Override + public String getApplicationDataRoot() { + return tempDir.toString(); + } + + @Override + public String getApplicationDataSubFolder() { + final String suffix = "".equalsIgnoreCase(super.version) ? "" : "-"+super.version; + return ".scenebuilder"+suffix; } + } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index 8ca85af53..89877b0b0 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -39,8 +39,6 @@ import static org.junit.Assert.assertTrue; import java.io.File; -import java.io.IOException; -import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -54,7 +52,8 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.oracle.javafx.scenebuilder.app.AppPlatform; +import com.oracle.javafx.scenebuilder.app.PlatformDirectories; +import com.oracle.javafx.scenebuilder.app.PlatformSpecificDirectories; import com.oracle.javafx.scenebuilder.app.OperatingSystem; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; @@ -62,9 +61,9 @@ public class UserLibraryImporterTest { - private UserLibraryImporter classUnderTest; - private static Preferences testNode; + private UserLibraryImporter classUnderTest; + private PlatformDirectories appDirectories; @BeforeClass public static void setup() { @@ -81,15 +80,15 @@ public static void cleanup() throws Exception { @Test public void that_exception_is_thrown_when_null_argument_is_used() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), OperatingSystem.LINUX, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forLinux("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), appDirectories, testNode); assertThrows(NullPointerException.class, ()->classUnderTest.previousVersionUserLibraryPath(null)); } @Test public void that_empty_optional_is_provided_when_current_version_has_unsupported_format() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), OperatingSystem.LINUX, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forLinux("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), appDirectories, testNode); List candidates = List.of(Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1")); assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); @@ -97,16 +96,16 @@ public void that_empty_optional_is_provided_when_current_version_has_unsupported @Test public void that_empty_optional_is_provided_when_old_library_path_not_exists() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forLinux("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of(Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1")); assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); } @Test public void that_user_library_paths_are_detected_for_LINUX() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forLinux("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get(".scenebuilder-15.0.0"), Paths.get("Scene Builder"), @@ -123,8 +122,8 @@ public void that_user_library_paths_are_detected_for_LINUX() { @Test public void that_legacy_library_path_is_detected_for_LINUX() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.LINUX, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forLinux("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), @@ -138,8 +137,8 @@ public void that_legacy_library_path_is_detected_for_LINUX() { @Test public void that_user_library_paths_are_detected_for_WINDOWS() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.WINDOWS, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forWindows("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get("Scene Builder-17.0.0"), Paths.get("Scene Builder"), @@ -155,8 +154,8 @@ public void that_user_library_paths_are_detected_for_WINDOWS() { @Test public void that_legacy_library_path_is_detected_for_WINDOWS() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.WINDOWS, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forWindows("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), @@ -170,8 +169,8 @@ public void that_legacy_library_path_is_detected_for_WINDOWS() { @Test public void that_user_library_paths_are_detected_for_MACOS() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.MACOS, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forMacOS("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get("Scene Builder-17.0.0"), Paths.get("Scene Builder"), @@ -187,8 +186,8 @@ public void that_user_library_paths_are_detected_for_MACOS() { @Test public void that_legacy_library_path_is_detected_for_MACOS() { - classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), OperatingSystem.MACOS, - AppDirectories.usingAppPlatform(),testNode); + appDirectories = forMacOS("17.0.0-SNAPSHOT"); + classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0"), appDirectories, testNode); List candidates = List.of( Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), @@ -200,29 +199,6 @@ public void that_legacy_library_path_is_detected_for_MACOS() { assertEquals(Paths.get("Scene Builder"), x.get()); } - private AppDirectories creatLinuxLikeAppDirs(OperatingSystem os, String version) { - try { - Path tempDir = Files.createTempDirectory("SBUserLibImportTest_"); - return new AppDirectories() { - @Override - public String getUserLibraryFolder() { - return getApplicationDataRoot(os)+"/"+getApplicationDataSubFolder(os,version)+"/Library"; - } - @Override - public String getApplicationDataSubFolder(OperatingSystem os, String version) { - return AppPlatform.getApplicationDataSubFolder(os, version); - } - @Override - public String getApplicationDataRoot(OperatingSystem os) { - return tempDir.toString(); - } - }; - - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - @Test public void that_import_is_skipped_when_user_opted_out() { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); @@ -231,8 +207,8 @@ public void that_import_is_skipped_when_user_opted_out() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); - classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + PlatformDirectories appDirectories = new TestAppDirectories(os, version); + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); @@ -250,8 +226,8 @@ public void that_import_is_skipped_when_done_before() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); - classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + PlatformDirectories appDirectories = new TestAppDirectories(os, version); + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); @@ -268,11 +244,10 @@ public void that_old_files_are_imported() throws Exception { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - AppDirectories appDirectories = creatLinuxLikeAppDirs(os, version); - + PlatformDirectories appDirectories = new TestAppDirectories(os, version); // Prepare an old library - String dataRoot = appDirectories.getApplicationDataRoot(OperatingSystem.LINUX); + String dataRoot = appDirectories.getApplicationDataRoot(); Path oldLibrary = Paths.get(dataRoot).resolve(".scenebuilder").resolve("Library"); Files.createDirectories(oldLibrary); Path myJar = oldLibrary.resolve("MyCustomArtifact.txt"); @@ -281,7 +256,7 @@ public void that_old_files_are_imported() throws Exception { Path userLib = Paths.get(appDirectories.getUserLibraryFolder()); Files.createDirectories(userLib); - classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), os,appDirectories, testNode); + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); @@ -293,5 +268,26 @@ public void that_old_files_are_imported() throws Exception { assertEquals(1, importedFiles.length); assertEquals("MyCustomArtifact.txt", importedFiles[0].getName()); } + + @Test + public void that_correct_AppDirectories_are_used_by_default() { + if (testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null) !=null) { + testNode.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); + } + classUnderTest = new UserLibraryImporter(testNode); + assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); + } + + private static PlatformDirectories forLinux(String version) { + return new PlatformSpecificDirectories(OperatingSystem.LINUX, version); + } + + private static PlatformDirectories forMacOS(String version) { + return new PlatformSpecificDirectories(OperatingSystem.MACOS, version); + } + + private static PlatformDirectories forWindows(String version) { + return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index 057708ced..e79b72c7c 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -2,18 +2,23 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import java.util.HashSet; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.prefs.Preferences; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import javafx.scene.control.ButtonType; + public class PreferencesImporterTest { private PreferencesImporter classUnderTest; @@ -52,6 +57,8 @@ public void that_settings_are_copied_between_nodes() throws Exception { @Test public void that_user_will_be_only_asked_when_import_decision_was_not_made() throws Exception { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); + appPrefs.clear(); + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); assertTrue(classUnderTest.askForImport()); @@ -98,11 +105,61 @@ public void that_run_after_import_action_is_executed_in_tryImportingPreviousVers assertNotNull(appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null)); } - @Test(expected = NullPointerException.class) + @Test public void that_null_value_for_run_after_import_action_is_not_accepted() { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); - classUnderTest.runAfterImport(null); + assertThrows(NullPointerException.class, + ()->classUnderTest.runAfterImport(null)); + } + + @Test + public void that_user_interaction_is_executed_and_user_opt_out_decision_is_documented() throws Exception { + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); + appPrefs.clear(); + + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); + + Set interactionResponses = new HashSet<>(); + + Supplier> userInteraction = ()->{ + interactionResponses.add("alert-opened"); + return Optional.empty(); + }; + + classUnderTest.executeInteractionAndImport(userInteraction); + String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); + + assertTrue(interactionResponses.contains("alert-opened")); + assertTrue(documentedUserDecision.contains("-no-import")); + } + + @Test + public void that_user_interaction_is_executed_and_used_opted_in() throws Exception { + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); + appPrefs.clear(); + + AppVersion oldVersion = new AppVersion(0, 9); + Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); + olderPrefs.put("somekey", "1234"); + + Optional previousVersionSettings = Optional.of(new VersionedPreferences(oldVersion, olderPrefs)); + + classUnderTest = new PreferencesImporter(appPrefs, previousVersionSettings); + + Set interactionResponses = new HashSet<>(); + + Supplier> userInteraction = ()->{ + interactionResponses.add("alert-opened"); + return Optional.of(ButtonType.YES); + }; + + classUnderTest.askForActionAndRun(userInteraction); + String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); + + assertTrue(interactionResponses.contains("alert-opened")); + assertFalse(documentedUserDecision.contains("-no-import")); + assertNotEquals("", documentedUserDecision); } } From ef870f1fe0aebda0a48c03ab31987cf67b749486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Tue, 11 Jan 2022 08:37:55 +0100 Subject: [PATCH 06/42] Complemented Javadoc and renamed the application directory interface. --- ...ectories.java => ApplicationDirectories.java} | 6 +++--- .../app/PlatformSpecificDirectories.java | 2 +- .../app/library/user/UserLibraryImporter.java | 10 +++++----- .../library/user/UserLibraryImporterTest.java | 16 ++++++++-------- 4 files changed, 17 insertions(+), 17 deletions(-) rename app/src/main/java/com/oracle/javafx/scenebuilder/app/{PlatformDirectories.java => ApplicationDirectories.java} (96%) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java similarity index 96% rename from app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java rename to app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java index 5c65ca66c..d7f2f67e1 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java @@ -31,7 +31,7 @@ */ package com.oracle.javafx.scenebuilder.app; -public interface PlatformDirectories { +public interface ApplicationDirectories { /** * @return the root location where application data shall be stored on the @@ -76,8 +76,8 @@ public interface PlatformDirectories { String getApplicationDataFolder(); /** - * - * @return + * Logfile location + * @return The directory containing the application log file. */ String getLogFolder(); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 4038c97bd..801b86a9f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -8,7 +8,7 @@ * If a new operating system (OS) platform shall be supported, the OS specific * behavior is supposed to be implemented here in. */ -public class PlatformSpecificDirectories implements PlatformDirectories { +public class PlatformSpecificDirectories implements ApplicationDirectories { protected final OperatingSystem os; protected final String version; diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index 3a849b078..fe922ff2a 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -49,7 +49,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.javafx.scenebuilder.app.PlatformDirectories; +import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; import com.oracle.javafx.scenebuilder.app.AppPlatform; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; @@ -61,23 +61,23 @@ public class UserLibraryImporter { private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; private final Preferences preferences; - private final PlatformDirectories appDirectories; + private final ApplicationDirectories appDirectories; public UserLibraryImporter(Preferences applicationPreferences) { this(AppPlatform.getAppDirectories(), applicationPreferences); } - public UserLibraryImporter(PlatformDirectories directories, Preferences applicationPreferences) { + public UserLibraryImporter(ApplicationDirectories directories, Preferences applicationPreferences) { this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), directories, applicationPreferences); } - public UserLibraryImporter(Optional appVersion, PlatformDirectories appDirectories, Preferences applicationPreferences) { + public UserLibraryImporter(Optional appVersion, ApplicationDirectories appDirectories, Preferences applicationPreferences) { this.version = Objects.requireNonNull(appVersion); this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); } - protected PlatformDirectories getPlatformDirectories() { + protected ApplicationDirectories getPlatformDirectories() { return this.appDirectories; } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index 89877b0b0..f2eb63a37 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -52,7 +52,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.oracle.javafx.scenebuilder.app.PlatformDirectories; +import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; import com.oracle.javafx.scenebuilder.app.PlatformSpecificDirectories; import com.oracle.javafx.scenebuilder.app.OperatingSystem; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; @@ -63,7 +63,7 @@ public class UserLibraryImporterTest { private static Preferences testNode; private UserLibraryImporter classUnderTest; - private PlatformDirectories appDirectories; + private ApplicationDirectories appDirectories; @BeforeClass public static void setup() { @@ -207,7 +207,7 @@ public void that_import_is_skipped_when_user_opted_out() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - PlatformDirectories appDirectories = new TestAppDirectories(os, version); + ApplicationDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); @@ -226,7 +226,7 @@ public void that_import_is_skipped_when_done_before() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - PlatformDirectories appDirectories = new TestAppDirectories(os, version); + ApplicationDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); @@ -244,7 +244,7 @@ public void that_old_files_are_imported() throws Exception { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - PlatformDirectories appDirectories = new TestAppDirectories(os, version); + ApplicationDirectories appDirectories = new TestAppDirectories(os, version); // Prepare an old library String dataRoot = appDirectories.getApplicationDataRoot(); @@ -278,15 +278,15 @@ public void that_correct_AppDirectories_are_used_by_default() { assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); } - private static PlatformDirectories forLinux(String version) { + private static ApplicationDirectories forLinux(String version) { return new PlatformSpecificDirectories(OperatingSystem.LINUX, version); } - private static PlatformDirectories forMacOS(String version) { + private static ApplicationDirectories forMacOS(String version) { return new PlatformSpecificDirectories(OperatingSystem.MACOS, version); } - private static PlatformDirectories forWindows(String version) { + private static ApplicationDirectories forWindows(String version) { return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); } From d537ca35475c3125c7d1e32b30525dfb0a5b7ef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Tue, 11 Jan 2022 08:41:11 +0100 Subject: [PATCH 07/42] Adjusted lambda formatting following Scene Builder convention. --- .../scenebuilder/app/library/user/UserLibraryImporter.java | 6 +++--- .../javafx/scenebuilder/app/preferences/AppVersion.java | 2 +- .../scenebuilder/app/preferences/PreferencesImporter.java | 4 ++-- .../app/library/user/UserLibraryImporterTest.java | 2 +- .../app/preferences/PreferencesImporterTest.java | 6 +++--- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index fe922ff2a..ea5f4f125 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -180,8 +180,8 @@ protected Optional previousVersionUserLibraryPath(List candidates) { Objects.requireNonNull(candidates, "list of path candidates must not be null"); Map previousVersionLibDirs = collectPreviousVersionLibraryDirs(candidates); return previousVersionLibDirs.entrySet().stream() - .sorted((a,b)->b.getKey().compareTo(a.getKey())) - .map(e->e.getValue()) + .sorted((a,b) -> b.getKey().compareTo(a.getKey())) + .map(e -> e.getValue()) .findFirst(); } @@ -199,7 +199,7 @@ protected Map collectPreviousVersionLibraryDirs(List can String[] parts = name.split("[-]"); if (parts.length == 2) { AppVersion.fromString(parts[1]) - .ifPresent(v->{ + .ifPresent(v -> { if (v.compareTo(currentVersion) < 0) { sceneBuilderDirs.put(v, candidate); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java index 4ffbb0ffc..6ce74c19e 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java @@ -48,7 +48,7 @@ public record AppVersion(int major, int minor, Integer patch) implements Compara * @return {@link Comparator} sorting {@link AppVersion} instances in descending order. */ public static Comparator descending() { - return (a,b)->b.compareTo(a); + return (a,b) -> b.compareTo(a); } public AppVersion(int major, int minor) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 999d7dda5..a5a4af42e 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -84,7 +84,7 @@ public class PreferencesImporter { public PreferencesImporter(Preferences applicationPreferences, Optional optionalSourceNode) { this.target = Objects.requireNonNull(applicationPreferences); this.optionalSourceNode = Objects.requireNonNull(optionalSourceNode); - this.actionAfterImport = ()->logger.log(Level.INFO, "Importing settings completed. No post-import action defined."); + this.actionAfterImport = () -> logger.log(Level.INFO, "Importing settings completed. No post-import action defined."); } /** @@ -197,7 +197,7 @@ public void runAfterImport(Runnable action) { * The question will only appear in cases where previous version settings exist and the user decision has not been saved yet. */ public void askForActionAndRun() { - Supplier> alertInteraction = ()->{ + Supplier> alertInteraction = () -> { SBAlert customAlert = new SBAlert(AlertType.CONFIRMATION, ButtonType.YES, ButtonType.NO); customAlert.initOwner(null); customAlert.setTitle("Gluon Scene Builder"); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index f2eb63a37..298fb6dfc 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -83,7 +83,7 @@ public void that_exception_is_thrown_when_null_argument_is_used() { appDirectories = forLinux("17.0.0-SNAPSHOT"); classUnderTest = new UserLibraryImporter(AppVersion.fromString("17.0.0-SNAPSHOT"), appDirectories, testNode); assertThrows(NullPointerException.class, - ()->classUnderTest.previousVersionUserLibraryPath(null)); + () -> classUnderTest.previousVersionUserLibraryPath(null)); } @Test public void that_empty_optional_is_provided_when_current_version_has_unsupported_format() { diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index e79b72c7c..89dd6b0bb 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -110,7 +110,7 @@ public void that_null_value_for_run_after_import_action_is_not_accepted() { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); assertThrows(NullPointerException.class, - ()->classUnderTest.runAfterImport(null)); + () -> classUnderTest.runAfterImport(null)); } @Test @@ -122,7 +122,7 @@ public void that_user_interaction_is_executed_and_user_opt_out_decision_is_docum Set interactionResponses = new HashSet<>(); - Supplier> userInteraction = ()->{ + Supplier> userInteraction = () -> { interactionResponses.add("alert-opened"); return Optional.empty(); }; @@ -149,7 +149,7 @@ public void that_user_interaction_is_executed_and_used_opted_in() throws Excepti Set interactionResponses = new HashSet<>(); - Supplier> userInteraction = ()->{ + Supplier> userInteraction = () -> { interactionResponses.add("alert-opened"); return Optional.of(ButtonType.YES); }; From dad37c2635464a75b29a4275330a8b1e2aeabc9b Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Tue, 11 Jan 2022 23:42:06 +0100 Subject: [PATCH 08/42] Spelling correction. --- .../oracle/javafx/scenebuilder/app/preferences/AppVersion.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java index 6ce74c19e..344641bd0 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java @@ -36,7 +36,7 @@ /** * A record type to work with Scene Builder versions following the semantic - * versioning schema. Patch versions can be null and will be ignored then. + * version schema. Patch versions can be null and will be ignored then. * * Version numbers can be sorted, where major beats minor beats patch versions. * Version numbers with patch versions will be considered as higher (or newer) than version numbers without patch numbers. From ad07a68e78566563bcc397345ca1e0d66a61ece1 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 12 Jan 2022 00:04:08 +0100 Subject: [PATCH 09/42] Added unsupported operation exceptions for cases where the OS is unknown. Updated Javadoc. Updated OperatingSystem enum. Log directory is no longer version specific (the log file is, so that the .scenebuilder directory can remain as is). --- .../app/ApplicationDirectories.java | 20 ++++--- .../scenebuilder/app/OperatingSystem.java | 31 ++++++---- .../app/PlatformSpecificDirectories.java | 56 +++++++++++++++---- .../scenebuilder/app/AppPlatformTest.java | 4 +- 4 files changed, 78 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java index d7f2f67e1..f5eb452c5 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java @@ -32,13 +32,13 @@ package com.oracle.javafx.scenebuilder.app; public interface ApplicationDirectories { - + /** * @return the root location where application data shall be stored on the * corresponding platform. */ String getApplicationDataRoot(); - + /** * Scene Builder provides the option to hold custom JAR files in a user library. * This method provides the full path to the location of the user library @@ -46,7 +46,7 @@ public interface ApplicationDirectories { * the application data folder. */ String getUserLibraryFolder(); - + /** * @return Provides the name of the by default version specific application data * sub directory. This folder is will be located inside the application @@ -57,7 +57,9 @@ public interface ApplicationDirectories { /** * In previous versions, Scene Builder stored its files in a directory without * version number. Hence, in some cases it might be helpful to control when the - * version number is used or not. + * version number is used or not. To obtain this folder without version number + * can be beneficial in cases, where one wants to search the application data root + * folder for other existing settings of Scene Builder. * * @param includeVersion If true, the version number might be a part of the sub * folder name. @@ -65,7 +67,7 @@ public interface ApplicationDirectories { * without version information. */ String getApplicationDataSubFolder(boolean includeVersion); - + /** * The application data folder is usually a child folder inside the application * data root. @@ -74,12 +76,16 @@ public interface ApplicationDirectories { * where settings and arbitrary files can be placed. */ String getApplicationDataFolder(); - + /** * Logfile location * @return The directory containing the application log file. */ String getLogFolder(); - + + /** + * Location of Scene Builders Message Box + * @return The directory where the Message Box is saved. + */ String getMessageBoxFolder(); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java index a6a2bb11b..0060a4ca1 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java @@ -34,9 +34,21 @@ import java.util.Locale; public enum OperatingSystem { - MACOS, - WINDOWS, - LINUX; + MACOS("mac"), + WINDOWS("windows"), + LINUX("linux"); + + private final String osName; + + OperatingSystem(String osName) { + this.osName = osName; + } + + private boolean matches() { + String currentOsName = System.getProperty("os.name").toLowerCase(Locale.ROOT); + return currentOsName.contains(osName); + } + /** * Obtains the operating system type from system property os.name. * @@ -44,15 +56,10 @@ public enum OperatingSystem { * @throws UnsupportedOperationException in case of an unknown operating system */ public static OperatingSystem get() { - String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); - if (osName.contains("linux")) { - return OperatingSystem.LINUX; - } - if (osName.contains("mac")) { - return OperatingSystem.MACOS; - } - if (osName.contains("windows")) { - return OperatingSystem.WINDOWS; + for (OperatingSystem os : OperatingSystem.values()) { + if (os.matches()) { + return os; + } } throw new UnsupportedOperationException("Unknown operating system platform!"); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 801b86a9f..0387a8fcc 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -11,27 +11,40 @@ public class PlatformSpecificDirectories implements ApplicationDirectories { protected final OperatingSystem os; protected final String version; - + public PlatformSpecificDirectories(OperatingSystem os, String appVersion) { this.os = Objects.requireNonNull(os); this.version = appVersion; } - + + /** + * The application data folder is usually a child folder inside the application + * data root. + * + * @return the exact location, specifically for this version of Scene Builder, + * where settings and arbitrary files can be placed. + * @throws UnsupportedOperationException in case of the operating system is unknown + */ @Override public String getApplicationDataFolder() { String appDataRoot = getApplicationDataRoot(); String appDataSubFolder = getApplicationDataSubFolder(); return appDataRoot+"/"+appDataSubFolder; } - + + /** + * @return the root location where application data shall be stored on the + * corresponding platform. + * @throws UnsupportedOperationException in case of the operating system is unknown + */ @Override public String getApplicationDataRoot() { - switch (os) { - case WINDOWS: return System.getenv("APPDATA") + "\\"; - case MACOS: return System.getProperty("user.home") + "/Library/Application Support/"; - case LINUX: return System.getProperty("user.home") + "/"; - default: return null; - } + return switch (os) { + case WINDOWS -> System.getenv("APPDATA") + "\\"; + case MACOS -> System.getProperty("user.home") + "/Library/Application Support/"; + case LINUX -> System.getProperty("user.home") + "/"; + default -> throw new UnsupportedOperationException("Unknown operating system platform!"); + }; } @Override @@ -39,11 +52,30 @@ public String getUserLibraryFolder() { return getApplicationDataFolder() + "/Library"; } + /** + * @return Provides the name of the by default version specific application data + * sub directory. This folder is will be located inside the application + * data root folder. + * @throws UnsupportedOperationException in case of the operating system is unknown + */ @Override public String getApplicationDataSubFolder() { return getApplicationDataSubFolder(true); } - + + /** + * In previous versions, Scene Builder stored its files in a directory without + * version number. Hence, in some cases it might be helpful to control when the + * version number is used or not. To obtain this folder without version number + * can be beneficial in cases, where one wants to search the application data root + * folder for other existing settings of Scene Builder. + * + * @param includeVersion If true, the version number might be a part of the sub + * folder name. + * @return Provides the name of the application data sub directory with or + * without version information. + * @throws UnsupportedOperationException in case of the operating system is unknown + */ @Override public String getApplicationDataSubFolder(boolean includeVersion) { final String appName = "Scene Builder"; //NOI18N @@ -53,14 +85,14 @@ public String getApplicationDataSubFolder(boolean includeVersion) { case WINDOWS -> appName+suffix; case MACOS -> appName+suffix; case LINUX -> ".scenebuilder"+suffix; - default -> null; + default -> throw new UnsupportedOperationException("Unknown operating system platform!"); }; } @Override public String getLogFolder() { return Paths.get(System.getProperty("user.home"), - ".scenebuilder-"+version, "logs").toString(); + ".scenebuilder", "logs").toString(); } @Override diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index 1f116bf5c..bc33219a5 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -84,10 +84,10 @@ public void that_user_library_resides_in_application_settings_folder() { } @Test - public void that_logfile_stored_in_userhome_version_specific_log_dir() { + public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { Path logDir = Paths.get(AppPlatform.getLogFolder()); Path expected = Paths.get(System.getProperty("user.home")) - .resolve(".scenebuilder-"+appVersion) + .resolve(".scenebuilder") .resolve("logs"); assertEquals(expected, logDir); } From 97d8d2157e2f7aae875229b2026144533c601512 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 12 Jan 2022 00:07:36 +0100 Subject: [PATCH 10/42] Javadoc update --- .../com/oracle/javafx/scenebuilder/app/OperatingSystem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java index 0060a4ca1..5c5f14226 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java @@ -33,6 +33,9 @@ import java.util.Locale; +/** + * Detects the used operating system and provides an enum item for it. + */ public enum OperatingSystem { MACOS("mac"), WINDOWS("windows"), @@ -51,7 +54,6 @@ private boolean matches() { /** * Obtains the operating system type from system property os.name. - * * @return {@link OperatingSystem} * @throws UnsupportedOperationException in case of an unknown operating system */ From d35f78f2b834177f77f84ed69005a51c9ba761e5 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 12 Jan 2022 00:11:41 +0100 Subject: [PATCH 11/42] Added test for OS detection. --- .../scenebuilder/app/OperatingSystemTest.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java new file mode 100644 index 000000000..b99d36d11 --- /dev/null +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java @@ -0,0 +1,31 @@ +package com.oracle.javafx.scenebuilder.app; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; + +import org.junit.Test; + +public class OperatingSystemTest { + + @Test + public void that_MacOS_is_properly_detected() { + assumeTrue("MacOS", System.getProperty("os.name").toLowerCase().contains("mac")); + OperatingSystem os = OperatingSystem.get(); + assertEquals(OperatingSystem.MACOS, os); + } + + @Test + public void that_Windows_is_properly_detected() { + assumeTrue("Windows", System.getProperty("os.name").toLowerCase().contains("windows")); + OperatingSystem os = OperatingSystem.get(); + assertEquals(OperatingSystem.WINDOWS, os); + } + + @Test + public void that_Linux_is_properly_detected() { + assumeTrue("Linux", System.getProperty("os.name").toLowerCase().contains("linux")); + OperatingSystem os = OperatingSystem.get(); + assertEquals(OperatingSystem.LINUX, os); + } + +} From 9681d4e1ea574cd3a95ca4cc2f18da1d9907e5cc Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 12 Jan 2022 00:14:47 +0100 Subject: [PATCH 12/42] Formatting --- .../library/user/UserLibraryImporterTest.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index 298fb6dfc..c0cfd37ac 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -72,12 +72,12 @@ public static void setup() { .node("com/oracle/javafx/scenebuilder/app/preferences"); PrefsHelper.removeAllChildNodes(testNode); } - - @AfterClass + + @AfterClass public static void cleanup() throws Exception { Preferences.userRoot().node("USR_LIB_IMPORTER_TEST").removeNode(); } - + @Test public void that_exception_is_thrown_when_null_argument_is_used() { appDirectories = forLinux("17.0.0-SNAPSHOT"); @@ -85,6 +85,7 @@ public void that_exception_is_thrown_when_null_argument_is_used() { assertThrows(NullPointerException.class, () -> classUnderTest.previousVersionUserLibraryPath(null)); } + @Test public void that_empty_optional_is_provided_when_current_version_has_unsupported_format() { appDirectories = forLinux("17.0.0-SNAPSHOT"); @@ -93,7 +94,7 @@ public void that_empty_optional_is_provided_when_current_version_has_unsupported Paths.get("Scene Builder-19.0.1")); assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); } - + @Test public void that_empty_optional_is_provided_when_old_library_path_not_exists() { appDirectories = forLinux("17.0.0-SNAPSHOT"); @@ -102,6 +103,7 @@ public void that_empty_optional_is_provided_when_old_library_path_not_exists() { Paths.get("Scene Builder-19.0.1")); assertTrue(classUnderTest.previousVersionUserLibraryPath(candidates).isEmpty()); } + @Test public void that_user_library_paths_are_detected_for_LINUX() { appDirectories = forLinux("17.0.0-SNAPSHOT"); @@ -119,7 +121,7 @@ public void that_user_library_paths_are_detected_for_LINUX() { assertFalse(x.isEmpty()); assertEquals(Paths.get(".scenebuilder-15.0.0"), x.get()); } - + @Test public void that_legacy_library_path_is_detected_for_LINUX() { appDirectories = forLinux("17.0.0-SNAPSHOT"); @@ -134,7 +136,7 @@ public void that_legacy_library_path_is_detected_for_LINUX() { assertFalse(x.isEmpty()); assertEquals(Paths.get(".scenebuilder"), x.get()); } - + @Test public void that_user_library_paths_are_detected_for_WINDOWS() { appDirectories = forWindows("17.0.0-SNAPSHOT"); @@ -151,7 +153,7 @@ public void that_user_library_paths_are_detected_for_WINDOWS() { assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); } - + @Test public void that_legacy_library_path_is_detected_for_WINDOWS() { appDirectories = forWindows("17.0.0-SNAPSHOT"); @@ -166,7 +168,7 @@ public void that_legacy_library_path_is_detected_for_WINDOWS() { assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder"), x.get()); } - + @Test public void that_user_library_paths_are_detected_for_MACOS() { appDirectories = forMacOS("17.0.0-SNAPSHOT"); @@ -183,7 +185,7 @@ public void that_user_library_paths_are_detected_for_MACOS() { assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); } - + @Test public void that_legacy_library_path_is_detected_for_MACOS() { appDirectories = forMacOS("17.0.0-SNAPSHOT"); @@ -198,7 +200,7 @@ public void that_legacy_library_path_is_detected_for_MACOS() { assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder"), x.get()); } - + @Test public void that_import_is_skipped_when_user_opted_out() { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); @@ -217,8 +219,7 @@ public void that_import_is_skipped_when_user_opted_out() { assertEquals("2022-01-05T20:14-no-import", documentedResult); assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); } - - + @Test public void that_import_is_skipped_when_done_before() { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); @@ -236,7 +237,7 @@ public void that_import_is_skipped_when_done_before() { assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); } - + @Test public void that_old_files_are_imported() throws Exception { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); @@ -268,7 +269,7 @@ public void that_old_files_are_imported() throws Exception { assertEquals(1, importedFiles.length); assertEquals("MyCustomArtifact.txt", importedFiles[0].getName()); } - + @Test public void that_correct_AppDirectories_are_used_by_default() { if (testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null) !=null) { @@ -277,17 +278,16 @@ public void that_correct_AppDirectories_are_used_by_default() { classUnderTest = new UserLibraryImporter(testNode); assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); } - + private static ApplicationDirectories forLinux(String version) { return new PlatformSpecificDirectories(OperatingSystem.LINUX, version); } - + private static ApplicationDirectories forMacOS(String version) { return new PlatformSpecificDirectories(OperatingSystem.MACOS, version); } - + private static ApplicationDirectories forWindows(String version) { return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); } - } From 13e2025bc992e21b2ea62bffdd9ca4a835ca9e18 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Wed, 12 Jan 2022 00:20:18 +0100 Subject: [PATCH 13/42] TODO added --- .../javafx/scenebuilder/app/PlatformSpecificDirectories.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 0387a8fcc..4167afdbe 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -3,6 +3,10 @@ import java.nio.file.Paths; import java.util.Objects; +/* + * TODO: Replace this with one implementation per platform. + * With that, all the conditions/switches will vanish. + */ /** * This is where all relevant application directories and paths are defined. * If a new operating system (OS) platform shall be supported, the OS specific From 71032cafa45e004ca671559c24f6de1680b33bbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Wed, 12 Jan 2022 22:57:32 +0100 Subject: [PATCH 14/42] Formatting adjusted to match master --- .../com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index 0113a30e4..bc2975841 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -785,7 +785,9 @@ private void performExit() { private enum ACTION { START("log.start"), STOP("log.stop"); + private final String logKey; + ACTION(String logKey) { this.logKey = logKey; } From b12356be9da42a0fd0d6a006295f1e887ea11593 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 17:39:33 +0100 Subject: [PATCH 15/42] Rework --- .../com/oracle/javafx/scenebuilder/app/OperatingSystem.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java index 5c5f14226..a8d61c326 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java @@ -48,8 +48,9 @@ public enum OperatingSystem { } private boolean matches() { - String currentOsName = System.getProperty("os.name").toLowerCase(Locale.ROOT); - return currentOsName.contains(osName); + return System.getProperty("os.name") + .toLowerCase(Locale.ROOT) + .contains(osName); } /** From e867fb2dccb1025df7f643b1db2c0001695761ba Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 17:42:44 +0100 Subject: [PATCH 16/42] Refactor --- .../oracle/javafx/scenebuilder/app/AppPlatform.java | 13 +++---------- .../app/about/AboutWindowController.java | 8 +++++--- .../app/menubar/DebugMenuController.java | 4 ++-- .../javafx/scenebuilder/app/AppPlatformTest.java | 12 ++++++------ 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 046535c61..87c2cba87 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -49,16 +49,9 @@ */ public class AppPlatform { private static MessageBox messageBox; - private static PlatformSpecificDirectories platformSpecificDirectories; - - public static synchronized String getApplicationDataFolder() { - if (platformSpecificDirectories == null) { - platformSpecificDirectories = getAppDirectories(); - } - return platformSpecificDirectories.getApplicationDataFolder(); - } - - public static synchronized PlatformSpecificDirectories getAppDirectories() { + private static ApplicationDirectories platformSpecificDirectories; + + public static synchronized ApplicationDirectories getAppDirectories() { final String appVersion = getApplicationVersion(); if (platformSpecificDirectories == null) { OperatingSystem os = OperatingSystem.get(); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java index 98a953df3..dd00ff122 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2021, Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates, 2015, Gluon. * All rights reserved. Use is subject to license terms. * @@ -33,6 +33,7 @@ package com.oracle.javafx.scenebuilder.app.about; import com.oracle.javafx.scenebuilder.app.AppPlatform; +import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; import com.oracle.javafx.scenebuilder.app.SceneBuilderApp; import com.oracle.javafx.scenebuilder.app.i18n.I18N; import com.oracle.javafx.scenebuilder.app.util.AppSettings; @@ -143,13 +144,14 @@ private String getAboutText() { } private StringBuilder getApplicationDirectoriesParagraph() { + ApplicationDirectories appDirs = AppPlatform.getAppDirectories(); return new StringBuilder(I18N.getString("about.app.data.directory")) .append("\n\t") // NOI18N - .append(Paths.get(AppPlatform.getApplicationDataFolder()).normalize()) //NOI18N + .append(Paths.get(appDirs.getApplicationDataFolder()).normalize()) //NOI18N .append("\n\n") //NOI18N .append(I18N.getString("about.app.user.library")) .append("\n\t") //NOI18N - .append(Paths.get(AppPlatform.getUserLibraryFolder()).normalize()) //NOI18N + .append(Paths.get(appDirs.getUserLibraryFolder()).normalize()) //NOI18N .append("\n\n") //NOI18N .append(I18N.getString("about.app.program.directory")) .append("\n\t") //NOI18N diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java index 34b2bf337..99354c190 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2014, Oracle and/or its affiliates. + * Copyright (c) 2012, 2022, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -70,7 +70,7 @@ public DebugMenuController(DocumentWindowController documentWindowController) { * User Library Folder */ final String applicationDataFolder - = AppPlatform.getApplicationDataFolder(); + = AppPlatform.getAppDirectories().getApplicationDataFolder(); final MenuItem libraryFolderMenuItem = new MenuItem(); libraryFolderMenuItem.setText(applicationDataFolder); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index bc33219a5..ec4da6465 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -48,8 +48,8 @@ public class AppPlatformTest { @Test public void that_windows_application_data_directory_is_specific_to_version() { - assumeTrue(getOsName().contains("windows")); - Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + assumeTrue(getOsName().contains("windows")); + Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); Path expected = Paths.get(System.getenv("APPDATA")) .resolve("Scene Builder-"+appVersion); assertEquals(expected, appDataDir); @@ -58,7 +58,7 @@ public void that_windows_application_data_directory_is_specific_to_version() { @Test public void that_mac_application_data_directory_is_specific_to_version() { assumeTrue(getOsName().contains("mac")); - Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); Path expected = Paths.get(System.getProperty("user.home")) .resolve("Library") .resolve("Application Support") @@ -69,7 +69,7 @@ public void that_mac_application_data_directory_is_specific_to_version() { @Test public void that_linux_application_data_directory_is_specific_to_version() { assumeTrue(getOsName().contains("linux")); - Path appDataDir = Paths.get(AppPlatform.getApplicationDataFolder()); + Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); Path expected = Paths.get(System.getProperty("user.home")) .resolve(".scenebuilder-"+appVersion); assertEquals(expected, appDataDir); @@ -78,7 +78,7 @@ public void that_linux_application_data_directory_is_specific_to_version() { @Test public void that_user_library_resides_in_application_settings_folder() { Path userLibraryFolder = Paths.get(AppPlatform.getUserLibraryFolder()); - Path expected = Paths.get(AppPlatform.getApplicationDataFolder()) + Path expected = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()) .resolve("Library"); assertEquals(expected, userLibraryFolder); } @@ -95,7 +95,7 @@ public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { @Test public void that_messagebox_is_placed_in_application_dir() { Path mboxDir = Paths.get(AppPlatform.getMessageBoxFolder()); - Path expected = Paths.get(AppPlatform.getApplicationDataFolder()) + Path expected = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()) .resolve("MB"); assertEquals(expected, mboxDir); } From e3ae7830e807882c99d5901175625578d136de5f Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 17:44:38 +0100 Subject: [PATCH 17/42] LibraryFolder refactored --- .../com/oracle/javafx/scenebuilder/app/AppPlatform.java | 7 ------- .../oracle/javafx/scenebuilder/app/SceneBuilderApp.java | 2 +- .../oracle/javafx/scenebuilder/app/AppPlatformTest.java | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 87c2cba87..96a8b5b2b 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -77,13 +77,6 @@ private static String getApplicationVersion() { assert !version.isBlank(); return version; } - - public static synchronized String getUserLibraryFolder() { - if (platformSpecificDirectories == null) { - platformSpecificDirectories = getAppDirectories(); - } - return platformSpecificDirectories.getUserLibraryFolder(); - } /** * Returns the directory path for logs. Default path is "${user.home}/.scenebuilder/logs/". diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index 1cf061428..ab0704a47 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -396,7 +396,7 @@ public void handleLaunch(List files) { // Creates the user library MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); - userLibrary = new UserLibrary(AppPlatform.getUserLibraryFolder(), + userLibrary = new UserLibrary(AppPlatform.getAppDirectories().getUserLibraryFolder(), () -> mavenPreferences.getArtifactsPathsWithDependencies(), () -> mavenPreferences.getArtifactsFilter()); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index ec4da6465..03695f032 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -77,7 +77,7 @@ public void that_linux_application_data_directory_is_specific_to_version() { @Test public void that_user_library_resides_in_application_settings_folder() { - Path userLibraryFolder = Paths.get(AppPlatform.getUserLibraryFolder()); + Path userLibraryFolder = Paths.get(AppPlatform.getAppDirectories().getUserLibraryFolder()); Path expected = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()) .resolve("Library"); assertEquals(expected, userLibraryFolder); From 359c066d8193c3f28ae1197ba1e5eec1f86c949c Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 17:46:29 +0100 Subject: [PATCH 18/42] Logfolder refactored out --- .../oracle/javafx/scenebuilder/app/AppPlatform.java | 13 +------------ .../app/PlatformSpecificDirectories.java | 4 ++++ .../app/about/AboutWindowController.java | 2 +- .../javafx/scenebuilder/app/AppPlatformTest.java | 2 +- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 96a8b5b2b..05d7bc90f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -78,17 +78,6 @@ private static String getApplicationVersion() { return version; } - /** - * Returns the directory path for logs. Default path is "${user.home}/.scenebuilder/logs/". - * @return Directory path for Scene Builder logs - */ - public static synchronized String getLogFolder() { - if (platformSpecificDirectories == null) { - platformSpecificDirectories = getAppDirectories(); - } - return platformSpecificDirectories.getLogFolder(); - } - protected static String getMessageBoxFolder() { if (platformSpecificDirectories == null) { platformSpecificDirectories = getAppDirectories(); @@ -127,7 +116,7 @@ private static synchronized boolean requestStartGeneric(AppNotificationHandler n try { Files.createDirectories(Paths.get(getMessageBoxFolder())); - Files.createDirectories(Paths.get(getLogFolder())); + Files.createDirectories(Paths.get(getAppDirectories().getLogFolder())); } catch(FileAlreadyExistsException x) { // Fine } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 4167afdbe..eb62accb9 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -93,6 +93,10 @@ public String getApplicationDataSubFolder(boolean includeVersion) { }; } + /** + * Returns the directory path for logs. Default path is "${user.home}/.scenebuilder/logs/". + * @return Directory path for Scene Builder logs + */ @Override public String getLogFolder() { return Paths.get(System.getProperty("user.home"), diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java index dd00ff122..90a1bd87d 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java @@ -183,7 +183,7 @@ private StringBuilder getVersionParagraph() { } private String getLogFilePath() { - StringBuilder sb = new StringBuilder(AppPlatform.getLogFolder()); + StringBuilder sb = new StringBuilder(AppPlatform.getAppDirectories().getLogFolder()); if (sb.charAt(sb.length() - 1) != File.separatorChar) { sb.append(File.separatorChar); } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index 03695f032..f4072b429 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -85,7 +85,7 @@ public void that_user_library_resides_in_application_settings_folder() { @Test public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { - Path logDir = Paths.get(AppPlatform.getLogFolder()); + Path logDir = Paths.get(AppPlatform.getAppDirectories().getLogFolder()); Path expected = Paths.get(System.getProperty("user.home")) .resolve(".scenebuilder") .resolve("logs"); From 9472f05a693f4fadfcf2f46832e463e3f419b8db Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 17:59:28 +0100 Subject: [PATCH 19/42] Refactoring --- .../javafx/scenebuilder/app/AppPlatform.java | 15 ++++----------- ...ectories.java => AppPlatformDirectories.java} | 8 +++----- .../app/PlatformSpecificDirectories.java | 9 ++++++--- .../app/about/AboutWindowController.java | 4 ++-- .../app/library/user/UserLibraryImporter.java | 10 +++++----- .../javafx/scenebuilder/app/AppPlatformTest.java | 13 ++++++++++--- .../library/user/UserLibraryImporterTest.java | 16 ++++++++-------- 7 files changed, 38 insertions(+), 37 deletions(-) rename app/src/main/java/com/oracle/javafx/scenebuilder/app/{ApplicationDirectories.java => AppPlatformDirectories.java} (94%) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 05d7bc90f..9c1441297 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -49,9 +49,9 @@ */ public class AppPlatform { private static MessageBox messageBox; - private static ApplicationDirectories platformSpecificDirectories; + private static PlatformSpecificDirectories platformSpecificDirectories; - public static synchronized ApplicationDirectories getAppDirectories() { + public static synchronized AppPlatformDirectories getAppDirectories() { final String appVersion = getApplicationVersion(); if (platformSpecificDirectories == null) { OperatingSystem os = OperatingSystem.get(); @@ -78,13 +78,6 @@ private static String getApplicationVersion() { return version; } - protected static String getMessageBoxFolder() { - if (platformSpecificDirectories == null) { - platformSpecificDirectories = getAppDirectories(); - } - return platformSpecificDirectories.getMessageBoxFolder(); - } - public static boolean requestStart(AppNotificationHandler notificationHandler, Application.Parameters parameters) throws IOException { if (EditorPlatform.isAssertionEnabled()) { @@ -115,14 +108,14 @@ private static synchronized boolean requestStartGeneric(AppNotificationHandler n assert messageBox == null; try { - Files.createDirectories(Paths.get(getMessageBoxFolder())); + Files.createDirectories(Paths.get(((PlatformSpecificDirectories)getAppDirectories()).getMessageBoxFolder())); Files.createDirectories(Paths.get(getAppDirectories().getLogFolder())); } catch(FileAlreadyExistsException x) { // Fine } final boolean result; - messageBox = new MessageBox<>(getMessageBoxFolder(), MessageBoxMessage.class, 1000 /* ms */); + messageBox = new MessageBox<>(((PlatformSpecificDirectories)getAppDirectories()).getMessageBoxFolder(), MessageBoxMessage.class, 1000 /* ms */); // Fix Start: Github Issue #301 final List parametersUnnamed = new ArrayList<>(parameters.getUnnamed()); if (OperatingSystem.get().equals(OperatingSystem.MACOS)) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java similarity index 94% rename from app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java rename to app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java index f5eb452c5..6f8b0396d 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/ApplicationDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java @@ -31,7 +31,7 @@ */ package com.oracle.javafx.scenebuilder.app; -public interface ApplicationDirectories { +public interface AppPlatformDirectories { /** * @return the root location where application data shall be stored on the @@ -83,9 +83,7 @@ public interface ApplicationDirectories { */ String getLogFolder(); - /** - * Location of Scene Builders Message Box - * @return The directory where the Message Box is saved. + /* MessageBox folder is not provided by this as MessageBox folder previously + * was package private, here all previously public directories are accessible. */ - String getMessageBoxFolder(); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index eb62accb9..c1f9b7a2f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -12,7 +12,7 @@ * If a new operating system (OS) platform shall be supported, the OS specific * behavior is supposed to be implemented here in. */ -public class PlatformSpecificDirectories implements ApplicationDirectories { +public class PlatformSpecificDirectories implements AppPlatformDirectories { protected final OperatingSystem os; protected final String version; @@ -103,8 +103,11 @@ public String getLogFolder() { ".scenebuilder", "logs").toString(); } - @Override - public String getMessageBoxFolder() { + /** + * Location of Scene Builders Message Box + * @return The directory where the Message Box is saved. + */ + protected String getMessageBoxFolder() { return getApplicationDataFolder() + "/MB"; } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java index 90a1bd87d..bfd621d95 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java @@ -33,7 +33,7 @@ package com.oracle.javafx.scenebuilder.app.about; import com.oracle.javafx.scenebuilder.app.AppPlatform; -import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; +import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.SceneBuilderApp; import com.oracle.javafx.scenebuilder.app.i18n.I18N; import com.oracle.javafx.scenebuilder.app.util.AppSettings; @@ -144,7 +144,7 @@ private String getAboutText() { } private StringBuilder getApplicationDirectoriesParagraph() { - ApplicationDirectories appDirs = AppPlatform.getAppDirectories(); + AppPlatformDirectories appDirs = AppPlatform.getAppDirectories(); return new StringBuilder(I18N.getString("about.app.data.directory")) .append("\n\t") // NOI18N .append(Paths.get(appDirs.getApplicationDataFolder()).normalize()) //NOI18N diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index ea5f4f125..8b89492a6 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -49,7 +49,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; +import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.AppPlatform; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; @@ -61,23 +61,23 @@ public class UserLibraryImporter { private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; private final Preferences preferences; - private final ApplicationDirectories appDirectories; + private final AppPlatformDirectories appDirectories; public UserLibraryImporter(Preferences applicationPreferences) { this(AppPlatform.getAppDirectories(), applicationPreferences); } - public UserLibraryImporter(ApplicationDirectories directories, Preferences applicationPreferences) { + public UserLibraryImporter(AppPlatformDirectories directories, Preferences applicationPreferences) { this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), directories, applicationPreferences); } - public UserLibraryImporter(Optional appVersion, ApplicationDirectories appDirectories, Preferences applicationPreferences) { + public UserLibraryImporter(Optional appVersion, AppPlatformDirectories appDirectories, Preferences applicationPreferences) { this.version = Objects.requireNonNull(appVersion); this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); } - protected ApplicationDirectories getPlatformDirectories() { + protected AppPlatformDirectories getPlatformDirectories() { return this.appDirectories; } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index f4072b429..85575289a 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -32,6 +32,7 @@ package com.oracle.javafx.scenebuilder.app; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; import java.nio.file.Path; @@ -91,11 +92,17 @@ public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { .resolve("logs"); assertEquals(expected, logDir); } - + + /* + * MessageBox does not seem to be public API, the directory was previously + * declared protected, hence here the cast is needed. + */ @Test public void that_messagebox_is_placed_in_application_dir() { - Path mboxDir = Paths.get(AppPlatform.getMessageBoxFolder()); - Path expected = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()) + assertTrue(AppPlatform.getAppDirectories() instanceof PlatformSpecificDirectories); + PlatformSpecificDirectories dirs = (PlatformSpecificDirectories) AppPlatform.getAppDirectories(); + Path mboxDir = Paths.get(dirs.getMessageBoxFolder()); + Path expected = Paths.get(dirs.getApplicationDataFolder()) .resolve("MB"); assertEquals(expected, mboxDir); } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index c0cfd37ac..f35b4dc8b 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -52,7 +52,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import com.oracle.javafx.scenebuilder.app.ApplicationDirectories; +import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.PlatformSpecificDirectories; import com.oracle.javafx.scenebuilder.app.OperatingSystem; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; @@ -63,7 +63,7 @@ public class UserLibraryImporterTest { private static Preferences testNode; private UserLibraryImporter classUnderTest; - private ApplicationDirectories appDirectories; + private AppPlatformDirectories appDirectories; @BeforeClass public static void setup() { @@ -209,7 +209,7 @@ public void that_import_is_skipped_when_user_opted_out() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - ApplicationDirectories appDirectories = new TestAppDirectories(os, version); + AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); @@ -227,7 +227,7 @@ public void that_import_is_skipped_when_done_before() { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - ApplicationDirectories appDirectories = new TestAppDirectories(os, version); + AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); classUnderTest.performImportWhenDesired(timestamp); @@ -245,7 +245,7 @@ public void that_old_files_are_imported() throws Exception { String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; - ApplicationDirectories appDirectories = new TestAppDirectories(os, version); + AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); // Prepare an old library String dataRoot = appDirectories.getApplicationDataRoot(); @@ -279,15 +279,15 @@ public void that_correct_AppDirectories_are_used_by_default() { assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); } - private static ApplicationDirectories forLinux(String version) { + private static AppPlatformDirectories forLinux(String version) { return new PlatformSpecificDirectories(OperatingSystem.LINUX, version); } - private static ApplicationDirectories forMacOS(String version) { + private static AppPlatformDirectories forMacOS(String version) { return new PlatformSpecificDirectories(OperatingSystem.MACOS, version); } - private static ApplicationDirectories forWindows(String version) { + private static AppPlatformDirectories forWindows(String version) { return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); } } From d3e6dfaa500f1564db16dc40ad13e02ab31712ac Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 18:13:15 +0100 Subject: [PATCH 20/42] Refactored so that Paths are used rather then strings everywhere --- .../javafx/scenebuilder/app/AppPlatform.java | 11 ++++---- .../app/AppPlatformDirectories.java | 10 ++++--- .../app/PlatformSpecificDirectories.java | 28 +++++++++---------- .../scenebuilder/app/SceneBuilderApp.java | 3 +- .../app/about/AboutWindowController.java | 6 ++-- .../app/library/user/UserLibraryImporter.java | 6 ++-- .../app/menubar/DebugMenuController.java | 2 +- .../scenebuilder/app/AppPlatformTest.java | 20 ++++++------- .../app/library/user/TestAppDirectories.java | 4 +-- .../library/user/UserLibraryImporterTest.java | 12 ++++---- 10 files changed, 51 insertions(+), 51 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 9c1441297..3c17fc5d8 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -38,7 +38,7 @@ import java.io.IOException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import javafx.application.Application; @@ -106,16 +106,17 @@ private static synchronized boolean requestStartGeneric(AppNotificationHandler n assert notificationHandler != null; assert parameters != null; assert messageBox == null; - + + Path mboxDir = ((PlatformSpecificDirectories)getAppDirectories()).getMessageBoxFolder(); try { - Files.createDirectories(Paths.get(((PlatformSpecificDirectories)getAppDirectories()).getMessageBoxFolder())); - Files.createDirectories(Paths.get(getAppDirectories().getLogFolder())); + Files.createDirectories(mboxDir); + Files.createDirectories(getAppDirectories().getLogFolder()); } catch(FileAlreadyExistsException x) { // Fine } final boolean result; - messageBox = new MessageBox<>(((PlatformSpecificDirectories)getAppDirectories()).getMessageBoxFolder(), MessageBoxMessage.class, 1000 /* ms */); + messageBox = new MessageBox<>(mboxDir.toAbsolutePath().toString(), MessageBoxMessage.class, 1000 /* ms */); // Fix Start: Github Issue #301 final List parametersUnnamed = new ArrayList<>(parameters.getUnnamed()); if (OperatingSystem.get().equals(OperatingSystem.MACOS)) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java index 6f8b0396d..bcb084377 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java @@ -31,13 +31,15 @@ */ package com.oracle.javafx.scenebuilder.app; +import java.nio.file.Path; + public interface AppPlatformDirectories { /** * @return the root location where application data shall be stored on the * corresponding platform. */ - String getApplicationDataRoot(); + Path getApplicationDataRoot(); /** * Scene Builder provides the option to hold custom JAR files in a user library. @@ -45,7 +47,7 @@ public interface AppPlatformDirectories { * directory. Typically, the user library directory (Library) is placed inside * the application data folder. */ - String getUserLibraryFolder(); + Path getUserLibraryFolder(); /** * @return Provides the name of the by default version specific application data @@ -75,13 +77,13 @@ public interface AppPlatformDirectories { * @return the exact location, specifically for this version of Scene Builder, * where settings and arbitrary files can be placed. */ - String getApplicationDataFolder(); + Path getApplicationDataFolder(); /** * Logfile location * @return The directory containing the application log file. */ - String getLogFolder(); + Path getLogFolder(); /* MessageBox folder is not provided by this as MessageBox folder previously * was package private, here all previously public directories are accessible. diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index c1f9b7a2f..9209b6d05 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -1,5 +1,6 @@ package com.oracle.javafx.scenebuilder.app; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Objects; @@ -30,10 +31,8 @@ public PlatformSpecificDirectories(OperatingSystem os, String appVersion) { * @throws UnsupportedOperationException in case of the operating system is unknown */ @Override - public String getApplicationDataFolder() { - String appDataRoot = getApplicationDataRoot(); - String appDataSubFolder = getApplicationDataSubFolder(); - return appDataRoot+"/"+appDataSubFolder; + public Path getApplicationDataFolder() { + return getApplicationDataRoot().resolve(getApplicationDataSubFolder()); } /** @@ -42,18 +41,18 @@ public String getApplicationDataFolder() { * @throws UnsupportedOperationException in case of the operating system is unknown */ @Override - public String getApplicationDataRoot() { + public Path getApplicationDataRoot() { return switch (os) { - case WINDOWS -> System.getenv("APPDATA") + "\\"; - case MACOS -> System.getProperty("user.home") + "/Library/Application Support/"; - case LINUX -> System.getProperty("user.home") + "/"; + case WINDOWS -> Paths.get(System.getenv("APPDATA")); + case MACOS -> Paths.get(System.getProperty("user.home")).resolve("Library/Application Support/"); + case LINUX -> Paths.get(System.getProperty("user.home")); default -> throw new UnsupportedOperationException("Unknown operating system platform!"); }; } @Override - public String getUserLibraryFolder() { - return getApplicationDataFolder() + "/Library"; + public Path getUserLibraryFolder() { + return getApplicationDataFolder().resolve("Library"); } /** @@ -98,16 +97,15 @@ public String getApplicationDataSubFolder(boolean includeVersion) { * @return Directory path for Scene Builder logs */ @Override - public String getLogFolder() { - return Paths.get(System.getProperty("user.home"), - ".scenebuilder", "logs").toString(); + public Path getLogFolder() { + return Paths.get(System.getProperty("user.home"),".scenebuilder", "logs"); } /** * Location of Scene Builders Message Box * @return The directory where the Message Box is saved. */ - protected String getMessageBoxFolder() { - return getApplicationDataFolder() + "/MB"; + protected Path getMessageBoxFolder() { + return getApplicationDataFolder().resolve("MB"); } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index ab0704a47..8fc2bea4e 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -396,7 +396,8 @@ public void handleLaunch(List files) { // Creates the user library MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); - userLibrary = new UserLibrary(AppPlatform.getAppDirectories().getUserLibraryFolder(), + String userLibDir = AppPlatform.getAppDirectories().getUserLibraryFolder().normalize().toString(); + userLibrary = new UserLibrary(userLibDir, () -> mavenPreferences.getArtifactsPathsWithDependencies(), () -> mavenPreferences.getArtifactsFilter()); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java index bfd621d95..cb011e2b0 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/about/AboutWindowController.java @@ -147,11 +147,11 @@ private StringBuilder getApplicationDirectoriesParagraph() { AppPlatformDirectories appDirs = AppPlatform.getAppDirectories(); return new StringBuilder(I18N.getString("about.app.data.directory")) .append("\n\t") // NOI18N - .append(Paths.get(appDirs.getApplicationDataFolder()).normalize()) //NOI18N + .append(appDirs.getApplicationDataFolder().normalize()) //NOI18N .append("\n\n") //NOI18N .append(I18N.getString("about.app.user.library")) .append("\n\t") //NOI18N - .append(Paths.get(appDirs.getUserLibraryFolder()).normalize()) //NOI18N + .append(appDirs.getUserLibraryFolder().normalize()) //NOI18N .append("\n\n") //NOI18N .append(I18N.getString("about.app.program.directory")) .append("\n\t") //NOI18N @@ -183,7 +183,7 @@ private StringBuilder getVersionParagraph() { } private String getLogFilePath() { - StringBuilder sb = new StringBuilder(AppPlatform.getAppDirectories().getLogFolder()); + StringBuilder sb = new StringBuilder(AppPlatform.getAppDirectories().getLogFolder().toString()); if (sb.charAt(sb.length() - 1) != File.separatorChar) { sb.append(File.separatorChar); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index 8b89492a6..ba067479d 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -34,7 +34,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.time.LocalDateTime; import java.util.Collections; @@ -111,12 +110,11 @@ protected void importPreviousVersionUserLibrary(LocalDateTime timestamp) { String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()); String importStatus = importDecision+USER_LIBRARY_IMPORT_COMPLETE; preferences.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,importStatus); - String appDataDir = appDirectories.getApplicationDataRoot(); - Path appData = Paths.get(appDataDir); + Path appData = appDirectories.getApplicationDataRoot(); List candidates = collectLibraryCandidates(appData); Optional oldSettingsDirectory = previousVersionUserLibraryPath(candidates); if (oldSettingsDirectory.isPresent()) { - Path userLib = Paths.get(appDirectories.getUserLibraryFolder()); + Path userLib = appDirectories.getUserLibraryFolder(); importUserLibraryContentsFrom(oldSettingsDirectory.get(), userLib); } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java index 99354c190..6949b75d3 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/DebugMenuController.java @@ -70,7 +70,7 @@ public DebugMenuController(DocumentWindowController documentWindowController) { * User Library Folder */ final String applicationDataFolder - = AppPlatform.getAppDirectories().getApplicationDataFolder(); + = AppPlatform.getAppDirectories().getApplicationDataFolder().normalize().toString(); final MenuItem libraryFolderMenuItem = new MenuItem(); libraryFolderMenuItem.setText(applicationDataFolder); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index 85575289a..5d499969f 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -50,7 +50,7 @@ public class AppPlatformTest { @Test public void that_windows_application_data_directory_is_specific_to_version() { assumeTrue(getOsName().contains("windows")); - Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); + Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getenv("APPDATA")) .resolve("Scene Builder-"+appVersion); assertEquals(expected, appDataDir); @@ -59,7 +59,7 @@ public void that_windows_application_data_directory_is_specific_to_version() { @Test public void that_mac_application_data_directory_is_specific_to_version() { assumeTrue(getOsName().contains("mac")); - Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); + Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getProperty("user.home")) .resolve("Library") .resolve("Application Support") @@ -70,7 +70,7 @@ public void that_mac_application_data_directory_is_specific_to_version() { @Test public void that_linux_application_data_directory_is_specific_to_version() { assumeTrue(getOsName().contains("linux")); - Path appDataDir = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()); + Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getProperty("user.home")) .resolve(".scenebuilder-"+appVersion); assertEquals(expected, appDataDir); @@ -78,15 +78,16 @@ public void that_linux_application_data_directory_is_specific_to_version() { @Test public void that_user_library_resides_in_application_settings_folder() { - Path userLibraryFolder = Paths.get(AppPlatform.getAppDirectories().getUserLibraryFolder()); - Path expected = Paths.get(AppPlatform.getAppDirectories().getApplicationDataFolder()) - .resolve("Library"); + Path userLibraryFolder = AppPlatform.getAppDirectories().getUserLibraryFolder(); + Path expected = AppPlatform.getAppDirectories() + .getApplicationDataFolder() + .resolve("Library"); assertEquals(expected, userLibraryFolder); } @Test public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { - Path logDir = Paths.get(AppPlatform.getAppDirectories().getLogFolder()); + Path logDir = AppPlatform.getAppDirectories().getLogFolder(); Path expected = Paths.get(System.getProperty("user.home")) .resolve(".scenebuilder") .resolve("logs"); @@ -101,9 +102,8 @@ public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { public void that_messagebox_is_placed_in_application_dir() { assertTrue(AppPlatform.getAppDirectories() instanceof PlatformSpecificDirectories); PlatformSpecificDirectories dirs = (PlatformSpecificDirectories) AppPlatform.getAppDirectories(); - Path mboxDir = Paths.get(dirs.getMessageBoxFolder()); - Path expected = Paths.get(dirs.getApplicationDataFolder()) - .resolve("MB"); + Path mboxDir = dirs.getMessageBoxFolder(); + Path expected = dirs.getApplicationDataFolder().resolve("MB"); assertEquals(expected, mboxDir); } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java index 2cce0ca87..1a02d5b67 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java @@ -55,8 +55,8 @@ private Path createDirectory() { } @Override - public String getApplicationDataRoot() { - return tempDir.toString(); + public Path getApplicationDataRoot() { + return tempDir; } @Override diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index f35b4dc8b..85ee2a924 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -217,7 +217,7 @@ public void that_import_is_skipped_when_user_opted_out() { assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-no-import", documentedResult); - assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); + assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); } @Test @@ -235,7 +235,7 @@ public void that_import_is_skipped_when_done_before() { assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); - assertNull(new File(appDirectories.getUserLibraryFolder()).listFiles()); + assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); } @Test @@ -248,13 +248,13 @@ public void that_old_files_are_imported() throws Exception { AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); // Prepare an old library - String dataRoot = appDirectories.getApplicationDataRoot(); - Path oldLibrary = Paths.get(dataRoot).resolve(".scenebuilder").resolve("Library"); + Path dataRoot = appDirectories.getApplicationDataRoot(); + Path oldLibrary = dataRoot.resolve(".scenebuilder").resolve("Library"); Files.createDirectories(oldLibrary); Path myJar = oldLibrary.resolve("MyCustomArtifact.txt"); Files.writeString(myJar, "NotaJarButSomeTestContent", StandardOpenOption.CREATE); - Path userLib = Paths.get(appDirectories.getUserLibraryFolder()); + Path userLib = appDirectories.getUserLibraryFolder(); Files.createDirectories(userLib); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); @@ -265,7 +265,7 @@ public void that_old_files_are_imported() throws Exception { assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); - File[] importedFiles = new File(appDirectories.getUserLibraryFolder()).listFiles(); + File[] importedFiles = appDirectories.getUserLibraryFolder().toFile().listFiles(); assertEquals(1, importedFiles.length); assertEquals("MyCustomArtifact.txt", importedFiles[0].getName()); } From 2809ae8913d486638c93ffed6077ff5136a576f3 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 18:17:07 +0100 Subject: [PATCH 21/42] Javadoc update --- .../app/PlatformSpecificDirectories.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 9209b6d05..8ac4cd025 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -56,6 +56,8 @@ public Path getUserLibraryFolder() { } /** + * Provides the name of the application data sub directory (with version number). + * * @return Provides the name of the by default version specific application data * sub directory. This folder is will be located inside the application * data root folder. @@ -67,17 +69,21 @@ public String getApplicationDataSubFolder() { } /** - * In previous versions, Scene Builder stored its files in a directory without - * version number. Hence, in some cases it might be helpful to control when the - * version number is used or not. To obtain this folder without version number - * can be beneficial in cases, where one wants to search the application data root - * folder for other existing settings of Scene Builder. + * Provides the name of the application data sub directory (with or without + * version number). + * + * In previous versions, Scene Builder stored its files in a + * directory without version number. Hence, in some cases it might be helpful to + * control when the version number is used or not. To obtain this folder without + * version number can be beneficial in cases, where one wants to search the + * application data root folder for other existing settings of Scene Builder. * * @param includeVersion If true, the version number might be a part of the sub * folder name. * @return Provides the name of the application data sub directory with or * without version information. - * @throws UnsupportedOperationException in case of the operating system is unknown + * @throws UnsupportedOperationException in case of the operating system is + * unknown */ @Override public String getApplicationDataSubFolder(boolean includeVersion) { From b377675e97668fd745b5197b057634eb6fb45ab0 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 18:33:24 +0100 Subject: [PATCH 22/42] Refactored handling of OS specifics. Eliminated UnsupportedOperationExceptions. --- .../scenebuilder/app/OperatingSystem.java | 21 ++++++++++--------- .../app/PlatformSpecificDirectories.java | 9 ++------ .../scenebuilder/app/OperatingSystemTest.java | 19 +++++++++++------ .../app/library/user/TestAppDirectories.java | 5 ++--- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java index a8d61c326..c1cb8dc11 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/OperatingSystem.java @@ -37,9 +37,11 @@ * Detects the used operating system and provides an enum item for it. */ public enum OperatingSystem { + MACOS("mac"), WINDOWS("windows"), - LINUX("linux"); + LINUX("linux"), + OTHER("other-os"); private final String osName; @@ -47,23 +49,22 @@ public enum OperatingSystem { this.osName = osName; } - private boolean matches() { - return System.getProperty("os.name") - .toLowerCase(Locale.ROOT) - .contains(osName); - } - /** * Obtains the operating system type from system property os.name. + * For unknown operating systems OTHER will be returned. * @return {@link OperatingSystem} - * @throws UnsupportedOperationException in case of an unknown operating system */ public static OperatingSystem get() { + return get(System.getProperty("os.name")); + } + + static OperatingSystem get(String osNameSytemProperty) { + String osNameProperty = osNameSytemProperty.toLowerCase(Locale.ROOT); for (OperatingSystem os : OperatingSystem.values()) { - if (os.matches()) { + if (osNameProperty.contains(os.osName)) { return os; } } - throw new UnsupportedOperationException("Unknown operating system platform!"); + return OTHER; } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java index 8ac4cd025..e183dd3c7 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/PlatformSpecificDirectories.java @@ -28,7 +28,6 @@ public PlatformSpecificDirectories(OperatingSystem os, String appVersion) { * * @return the exact location, specifically for this version of Scene Builder, * where settings and arbitrary files can be placed. - * @throws UnsupportedOperationException in case of the operating system is unknown */ @Override public Path getApplicationDataFolder() { @@ -38,7 +37,6 @@ public Path getApplicationDataFolder() { /** * @return the root location where application data shall be stored on the * corresponding platform. - * @throws UnsupportedOperationException in case of the operating system is unknown */ @Override public Path getApplicationDataRoot() { @@ -46,7 +44,7 @@ public Path getApplicationDataRoot() { case WINDOWS -> Paths.get(System.getenv("APPDATA")); case MACOS -> Paths.get(System.getProperty("user.home")).resolve("Library/Application Support/"); case LINUX -> Paths.get(System.getProperty("user.home")); - default -> throw new UnsupportedOperationException("Unknown operating system platform!"); + default -> Paths.get(System.getProperty("user.home")); }; } @@ -61,7 +59,6 @@ public Path getUserLibraryFolder() { * @return Provides the name of the by default version specific application data * sub directory. This folder is will be located inside the application * data root folder. - * @throws UnsupportedOperationException in case of the operating system is unknown */ @Override public String getApplicationDataSubFolder() { @@ -82,8 +79,6 @@ public String getApplicationDataSubFolder() { * folder name. * @return Provides the name of the application data sub directory with or * without version information. - * @throws UnsupportedOperationException in case of the operating system is - * unknown */ @Override public String getApplicationDataSubFolder(boolean includeVersion) { @@ -94,7 +89,7 @@ public String getApplicationDataSubFolder(boolean includeVersion) { case WINDOWS -> appName+suffix; case MACOS -> appName+suffix; case LINUX -> ".scenebuilder"+suffix; - default -> throw new UnsupportedOperationException("Unknown operating system platform!"); + default -> ".scenebuilder"+suffix; }; } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java index b99d36d11..98b144d51 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java @@ -7,25 +7,32 @@ public class OperatingSystemTest { + private OperatingSystem classUnderTest; + @Test public void that_MacOS_is_properly_detected() { assumeTrue("MacOS", System.getProperty("os.name").toLowerCase().contains("mac")); - OperatingSystem os = OperatingSystem.get(); - assertEquals(OperatingSystem.MACOS, os); + classUnderTest = OperatingSystem.get(); + assertEquals(OperatingSystem.MACOS, classUnderTest); } @Test public void that_Windows_is_properly_detected() { assumeTrue("Windows", System.getProperty("os.name").toLowerCase().contains("windows")); - OperatingSystem os = OperatingSystem.get(); - assertEquals(OperatingSystem.WINDOWS, os); + classUnderTest = OperatingSystem.get(); + assertEquals(OperatingSystem.WINDOWS, classUnderTest); } @Test public void that_Linux_is_properly_detected() { assumeTrue("Linux", System.getProperty("os.name").toLowerCase().contains("linux")); - OperatingSystem os = OperatingSystem.get(); - assertEquals(OperatingSystem.LINUX, os); + classUnderTest = OperatingSystem.get(); + assertEquals(OperatingSystem.LINUX, classUnderTest); } + @Test + public void that_other_is_returned_for_unknown_OS() { + classUnderTest = OperatingSystem.get("BeOS"); + assertEquals(OperatingSystem.OTHER, classUnderTest); + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java index 1a02d5b67..51f176550 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/TestAppDirectories.java @@ -45,7 +45,7 @@ public class TestAppDirectories extends PlatformSpecificDirectories { public TestAppDirectories(OperatingSystem os, String appVersion) { super(os, appVersion); } - + private Path createDirectory() { try { return Files.createTempDirectory("SBTEST_APPDATA_"); @@ -58,11 +58,10 @@ private Path createDirectory() { public Path getApplicationDataRoot() { return tempDir; } - + @Override public String getApplicationDataSubFolder() { final String suffix = "".equalsIgnoreCase(super.version) ? "" : "-"+super.version; return ".scenebuilder"+suffix; } - } From fda949b8b8a27d4299adc0d3b5495506f01dcf28 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 14 Jan 2022 18:40:43 +0100 Subject: [PATCH 23/42] Reverted formatting changes. --- .../kit/preferences/PreferencesRecordArtifact.java | 6 +++--- .../kit/preferences/PreferencesRecordRepository.java | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java index 5c31af9f1..6fd84a87e 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordArtifact.java @@ -44,9 +44,9 @@ public class PreferencesRecordArtifact { private final Preferences artifactsRootPreferences; private Preferences artifactPreferences; - private final static String GROUPID = "groupID"; - private final static String ARTIFACTID = "artifactId"; - private final static String VERSION = "version"; + private final static String GROUPID = "groupID"; + private final static String ARTIFACTID = "artifactId"; + private final static String VERSION = "version"; public final static String DEPENDENCIES = "dependencies"; public final static String FILTER = "filter"; public final static String PATH = "path"; diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java index 7ded2b299..5c0dc241b 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/preferences/PreferencesRecordRepository.java @@ -44,11 +44,11 @@ public class PreferencesRecordRepository { private final Preferences repositoriesRootPreferences; private Preferences repositoryPreferences; - public final static String REPO_ID = "ID"; - public final static String REPO_TYPE = "type"; - public final static String REPO_URL = "URL"; + public final static String REPO_ID = "ID"; + public final static String REPO_TYPE = "type"; + public final static String REPO_URL = "URL"; public final static String REPO_USER = "User"; - public final static String REPO_PASS = "Password"; + public final static String REPO_PASS = "Password"; private final Repository repository; From e8e447eecc073d56d4a60eec68ee5e1fda2d4ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Sat, 15 Jan 2022 11:13:00 +0100 Subject: [PATCH 24/42] Header cleanup. Formatting. Test naming. Javadoc. Minimal refactoring. --- .../javafx/scenebuilder/app/AppPlatform.java | 2 +- .../preferences/PreferencesController.java | 8 ++++- .../VersionedPreferencesFinder.java | 2 +- .../scenebuilder/app/AppPlatformTest.java | 17 +++++----- .../scenebuilder/app/OperatingSystemTest.java | 31 +++++++++++++++++++ .../preferences/PreferencesImporterTest.java | 2 +- 6 files changed, 51 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index 3c17fc5d8..a11c124a6 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2021, Gluon and/or its affiliates. + * Copyright (c) 2017, 2022, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java index 4ad371abe..fcd9a28db 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2012 Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * @@ -143,6 +143,12 @@ private PreferencesController(Preferences rootNode) { * * **************************************************************************/ + /** + * Provides an instance of Scene Builders {@link PreferencesController}. + * The {@link Preferences} node to be used is defined internally. + * + * @return {@link PreferencesController} + */ public static synchronized PreferencesController getSingleton() { return getSingleton(null); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java index 646cdb263..4d2cb8402 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Gluon and/or its affiliates. + * Copyright (c) 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index 5d499969f..547717fbe 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, Gluon and/or its affiliates. + * Copyright (c) 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -49,7 +49,7 @@ public class AppPlatformTest { @Test public void that_windows_application_data_directory_is_specific_to_version() { - assumeTrue(getOsName().contains("windows")); + assumeTrue("Windows", osNameContains("windows")); Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getenv("APPDATA")) .resolve("Scene Builder-"+appVersion); @@ -58,7 +58,7 @@ public void that_windows_application_data_directory_is_specific_to_version() { @Test public void that_mac_application_data_directory_is_specific_to_version() { - assumeTrue(getOsName().contains("mac")); + assumeTrue("MacOS", osNameContains("mac")); Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getProperty("user.home")) .resolve("Library") @@ -69,7 +69,7 @@ public void that_mac_application_data_directory_is_specific_to_version() { @Test public void that_linux_application_data_directory_is_specific_to_version() { - assumeTrue(getOsName().contains("linux")); + assumeTrue("Linux", osNameContains("linux")); Path appDataDir = AppPlatform.getAppDirectories().getApplicationDataFolder(); Path expected = Paths.get(System.getProperty("user.home")) .resolve(".scenebuilder-"+appVersion); @@ -96,7 +96,8 @@ public void that_logfile_stored_in_userhome_dot_scenebuilder_log_dir() { /* * MessageBox does not seem to be public API, the directory was previously - * declared protected, hence here the cast is needed. + * declared protected, hence here the cast is needed as it is not part + * of the AppPlatformDirectories interface. */ @Test public void that_messagebox_is_placed_in_application_dir() { @@ -107,7 +108,9 @@ public void that_messagebox_is_placed_in_application_dir() { assertEquals(expected, mboxDir); } - private String getOsName() { - return System.getProperty("os.name").toLowerCase(Locale.ROOT); + private boolean osNameContains(String expectedName) { + return System.getProperty("os.name") + .toLowerCase(Locale.ROOT) + .contains(expectedName); } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java index 98b144d51..7e47e9363 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java @@ -1,3 +1,34 @@ +/* + * Copyright (c) 2022, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package com.oracle.javafx.scenebuilder.app; import static org.junit.Assert.assertEquals; diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index 89dd6b0bb..5adb39e56 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -135,7 +135,7 @@ public void that_user_interaction_is_executed_and_user_opt_out_decision_is_docum } @Test - public void that_user_interaction_is_executed_and_used_opted_in() throws Exception { + public void that_user_interaction_is_executed_and_user_opted_in() throws Exception { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); appPrefs.clear(); From 71d06d953312b12da3069f0089cf3e901569dedd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Sat, 15 Jan 2022 11:23:05 +0100 Subject: [PATCH 25/42] Added proper prepartion for one PrefsImporter test. --- .../app/preferences/PreferencesImporterTest.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index 5adb39e56..e106a65af 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -73,15 +73,19 @@ public void that_user_will_be_only_asked_when_previous_version_settings_exist() Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); + // ensure the user was never asked before + appPrefs.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); assertTrue(classUnderTest.askForImportIfOlderSettingsExist()); - + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); - + classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); classUnderTest.saveTimestampWhenAskedForImport(); assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); + + } @Test From a8282605e7ccd9a81aac0c95cd20b96df73f94d6 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 16 Jan 2022 21:48:59 +0100 Subject: [PATCH 26/42] Added support for SNAPSHOT versions. --- .../app/preferences/AppVersion.java | 67 ++++++++++++++----- .../app/preferences/AppVersionTest.java | 36 ++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java index 344641bd0..ad5de4405 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersion.java @@ -32,6 +32,7 @@ package com.oracle.javafx.scenebuilder.app.preferences; import java.util.Comparator; +import java.util.Locale; import java.util.Optional; /** @@ -39,10 +40,17 @@ * version schema. Patch versions can be null and will be ignored then. * * Version numbers can be sorted, where major beats minor beats patch versions. - * Version numbers with patch versions will be considered as higher (or newer) than version numbers without patch numbers. - * This is regardless if there is no patch number or patch version is 0. Patch version 0 is higher than no patch version at all. + * Version numbers with patch versions will be considered as higher (or newer) + * than version numbers without patch numbers. This is regardless if there is no + * patch number or patch version is 0. Patch version 0 is higher than no patch + * version at all. If there is any kind of extension such as {@code -SNAPSHOT}, + * than the same version with an extension is considered as the older version. + * + * As of now, Scene Builder release versions follow the schema {@code 17.0.0} + * which is {@code major.minor.patch}. Snapshot versions declared such as + * {@code 17.0.0-SNAPSHOT}. */ -public record AppVersion(int major, int minor, Integer patch) implements Comparable { +public record AppVersion(int major, int minor, Integer patch, String extension) implements Comparable { /** * @return {@link Comparator} sorting {@link AppVersion} instances in descending order. @@ -50,9 +58,13 @@ public record AppVersion(int major, int minor, Integer patch) implements Compara public static Comparator descending() { return (a,b) -> b.compareTo(a); } - + + public AppVersion(int major, int minor, int patch) { + this(major, minor, patch, null); + } + public AppVersion(int major, int minor) { - this(major, minor, null); + this(major, minor, null, null); } @Override @@ -60,8 +72,12 @@ public int compareTo(AppVersion o) { int majorDiff = major - o.major; int minorDiff = minor - o.minor; int patchDiff = calcPatchDiff(o); + int extensionDiff = calcExtensionDiff(o); if (majorDiff == 0) { if (minorDiff == 0) { + if (patchDiff == 0) { + return extensionDiff; + } return patchDiff; } return minorDiff; @@ -79,6 +95,9 @@ public String toString() { builder.append("."); builder.append(patch); } + if (extension != null) { + builder.append(extension); + } return builder.toString(); } @@ -112,38 +131,54 @@ protected int calcPatchDiff(AppVersion o) { } return patch - o.patch; } - + + protected int calcExtensionDiff(AppVersion o) { + String thisExt = (extension == null) ? "" : extension; + String otherExt = (o.extension == null) ? "" : o.extension; + return -thisExt.compareToIgnoreCase(otherExt); + } /** - * Parses an optional AppVersion from any given String. - * Snapshot versions are not considered as valid. + * Parses an optional AppVersion from a given String. The supported version + * schema must follow: {@code major.minor.patch-extension} whereas major, minor + * and patch are supposed to be positive integers and the extension can be an + * arbitrary string. * * @param validVersion String representing a Scene Builder version. - * @return Empty optional when the String does not represent a valid version. If valid, then an optional AppVersion is returned. + * @return Empty optional when the String does not represent a valid version. If + * valid, then an optional AppVersion is returned. */ public static Optional fromString(String validVersion) { - String[] elements = validVersion.strip().split("[.]"); + String versionToParse = validVersion.toUpperCase(Locale.ROOT); + String extension = null; + int lastDot = validVersion.lastIndexOf('.'); + int startOfSnapshot = validVersion.indexOf("-"); + if (startOfSnapshot > lastDot+1) { + extension = validVersion.substring(startOfSnapshot, validVersion.length()); + versionToParse = validVersion.substring(0, startOfSnapshot); + } + String[] elements = versionToParse.strip().split("[.]"); return switch (elements.length) { - case 2 -> parseMajorMinor(elements); - case 3 -> parseMajorMinorPatch(elements); + case 2 -> parseMajorMinor(elements, extension); + case 3 -> parseMajorMinorPatch(elements, extension); default -> Optional.empty(); }; } - private static Optional parseMajorMinor(String[] elements) { + private static Optional parseMajorMinor(String[] elements, String extension) { try { int major = Integer.parseInt(elements[0]); int minor = Integer.parseInt(elements[1]); if (major < 0 || minor < 0) { return Optional.empty(); } - return Optional.of(new AppVersion(major, minor)); + return Optional.of(new AppVersion(major, minor, null, extension)); } catch (NumberFormatException nfe) { return Optional.empty(); } } - private static Optional parseMajorMinorPatch(String[] elements) { + private static Optional parseMajorMinorPatch(String[] elements, String extension) { try { int major = Integer.parseInt(elements[0]); int minor = Integer.parseInt(elements[1]); @@ -151,7 +186,7 @@ private static Optional parseMajorMinorPatch(String[] elements) { if (major < 0 || minor < 0 || patch < 0) { return Optional.empty(); } - return Optional.of(new AppVersion(major, minor, patch)); + return Optional.of(new AppVersion(major, minor, patch, extension)); } catch (NumberFormatException nfe) { return Optional.empty(); } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java index 7992a9266..17657ffa5 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java @@ -56,6 +56,22 @@ public void that_new_major_version_is_larger_than_older_major_version() { assertTrue(result > 0); } + @Test + public void that_snapshotversions_are_always_older_than_release_versions() { + AppVersion old = new AppVersion(17, 0, 0, "-SNAPSHOT"); + AppVersion moreRecent = new AppVersion(17, 0, 0); + int result = moreRecent.compareTo(old); + assertTrue(result > 0); + } + + @Test + public void that_snapshotversions_are_always_older_than_release_versions_major_minor() { + AppVersion old = new AppVersion(17, 0, null, "-SNAPSHOT"); + AppVersion moreRecent = new AppVersion(17, 0); + int result = moreRecent.compareTo(old); + assertTrue(result > 0); + } + @Test public void that_minor_version_is_used_when_major_is_equal() { AppVersion old = new AppVersion(16, 1, 0); @@ -81,18 +97,37 @@ public void that_a_version_can_be_parsed_from_string() { assertEquals(expected, parsedVersion.get()); } + @Test + public void that_snapshot_version_can_be_parsed_from_string() { + String validVersion = "42.12.12-SNAPSHOT"; + AppVersion expected = new AppVersion(42, 12, 12, "-SNAPSHOT"); + Optional parsedVersion = AppVersion.fromString(validVersion); + assertTrue(parsedVersion.isPresent()); + assertEquals(expected, parsedVersion.get()); + } + @Test public void that_2digit_versions_are_parsed() { assertTrue(AppVersion.fromString("2.0").isPresent()); assertTrue(AppVersion.fromString("2.0.").isPresent()); } + @Test + public void that_2digit_snapshot_versions_are_parsed() { + AppVersion expected = new AppVersion(42, 12, null, "-SNAPSHOT"); + Optional parsed = AppVersion.fromString("42.12-SNAPSHOT"); + assertTrue(parsed.isPresent()); + assertEquals(expected, parsed.get()); + } + @Test public void that_useful_toString_exists() { assertEquals("1.0", new AppVersion(1, 0).toString()); assertEquals("1.2", new AppVersion(1, 2).toString()); assertEquals("1.0.0", new AppVersion(1, 0, 0).toString()); assertEquals("1.0.1", new AppVersion(1, 0, 1).toString()); + assertEquals("1.0.1-SNAPSHOT", new AppVersion(1, 0, 1, "-SNAPSHOT").toString()); + assertEquals("1.0-SNAPSHOT", new AppVersion(1, 0, null, "-SNAPSHOT").toString()); } @Test @@ -102,6 +137,7 @@ public void that_empty_optionals_are_returned_from_illegal_version_strings() { assertFalse(AppVersion.fromString("1.-10.-1").isPresent()); assertFalse(AppVersion.fromString("1.1.-100").isPresent()); assertFalse(AppVersion.fromString("-1.-1.-1").isPresent()); + assertFalse(AppVersion.fromString("1.1.-1-SNAPSHOT").isPresent()); assertFalse(AppVersion.fromString(".1.1.1.").isPresent()); assertFalse(AppVersion.fromString("1..1").isPresent()); assertFalse(AppVersion.fromString("11").isPresent()); From f5fa41bd4340498914991e73daab1b8a9f7a3fb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Fri, 21 Jan 2022 15:37:54 +0100 Subject: [PATCH 27/42] Removed JCenter repository incl. related search. --- .../library/maven/preset/MavenPresets.java | 4 +- .../library/maven/search/JcenterSearch.java | 114 ------------------ .../library/maven/search/SearchService.java | 15 +-- 3 files changed, 2 insertions(+), 131 deletions(-) delete mode 100644 kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java index a4a97fae6..b36a9dbe3 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -38,14 +38,12 @@ public class MavenPresets { public static final String MAVEN = "Maven Central"; - public static final String JCENTER = "Jcenter"; public static final String SONATYPE = "Sonatype"; public static final String GLUON_NEXUS = "Gluon Nexus"; public static final String LOCAL = "Local"; private static final List REPOSITORIES = Arrays.asList( new Repository(MAVEN, "default", "https://repo1.maven.org/maven2/"), - new Repository(JCENTER, "default", "https://jcenter.bintray.com"), new Repository(SONATYPE + " (snapshots)", "default", "https://oss.sonatype.org/content/repositories/snapshots"), new Repository(SONATYPE + " (releases)", "default", "https://oss.sonatype.org/content/repositories/releases"), new Repository(GLUON_NEXUS + " (releases)", "default", "https://nexus.gluonhq.com/nexus/content/repositories/releases")); diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java deleted file mode 100644 index 0822d653c..000000000 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016, Gluon and/or its affiliates. - * All rights reserved. Use is subject to license terms. - * - * This file is available and licensed under the following license: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the distribution. - * - Neither the name of Oracle Corporation and Gluon nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.search; - -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.preset.MavenPresets; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonReader; -import static org.apache.commons.codec.binary.Base64.encodeBase64; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClients; -import org.eclipse.aether.artifact.DefaultArtifact; - -public class JcenterSearch implements Search { - - // bintray - - // This requires authentication: -// private static final String URL_PREFIX = "https://api.bintray.com/search/packages?name="; - // This doesn't require authentication, limited to 50 results: - private static final String URL_PREFIX = "https://api.bintray.com/search/packages/maven?q=*"; - private static final String URL_SUFFIX = "*"; - - private final HttpClient client; - private final String username; - private final String password; - - public JcenterSearch(String username, String password) { - client = HttpClients.createDefault(); - this.username = username; - this.password = password; - } - - @Override - public List getCoordinates(String query) { - final Map map = new HashMap<>(); - map.put("Repository", MavenPresets.JCENTER); - - try { - HttpGet request = new HttpGet(URL_PREFIX + query + URL_SUFFIX); - if (!username.isEmpty() && !password.isEmpty()) { - String authStringEnc = new String(encodeBase64((username + ":" + password).getBytes())); - request.addHeader("Authorization", "Basic " + authStringEnc); - } - request.setHeader("Accept", "application/json"); - HttpResponse response = client.execute(request); - try (JsonReader rdr = Json.createReader(response.getEntity().getContent())) { - JsonArray obj = rdr.readArray(); - if (obj != null && !obj.isEmpty()) { - return obj.getValuesAs(JsonObject.class) - .stream() - .map(o -> { - JsonArray ids = o.getJsonArray("system_ids"); - if (ids != null && !ids.isEmpty()) { - return ids.stream() - .map(j -> j.toString().replaceAll("\"", "") + ":" + MIN_VERSION) - .collect(Collectors.toList()); - } - return null; - }) - .filter(Objects::nonNull) - .flatMap(l -> l.stream()) - .distinct() - .map(gav -> new DefaultArtifact(gav, map)) - .collect(Collectors.toList()); - } - } - } catch (IOException ex) { - Logger.getLogger(JcenterSearch.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - -} diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java index 0437fd7bc..b9ffc29e7 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -111,7 +111,6 @@ protected Void call() throws Exception { tasks = Arrays.asList( createSearchTask(new MavenSearch()), createSearchTask(new NexusSearch(MavenPresets.SONATYPE, "http://oss.sonatype.org", "", "")), - createSearchTask(new JcenterSearch("", "")), createSearchTask(new NexusSearch(MavenPresets.GLUON_NEXUS, "https://nexus.gluonhq.com/nexus", "", "")), createSearchTask(new LocalSearch(userM2Repository))); @@ -177,18 +176,6 @@ private List getLatestVersions(Map getAllVersions(Map> mapArtifacts) { - List list = new ArrayList<>(); - mapArtifacts.forEach((s, l) -> { - l.stream() - .filter(a -> !a.getVersion().toLowerCase(Locale.ROOT).contains("javadoc")) - .filter(a -> !a.getVersion().toLowerCase(Locale.ROOT).contains("source")) - .forEach(list::add); - }); - return list; - } - private Version getVersion(String version) { Version v1 = null; try { From 1c0408796f9d9bbe007b87bf064398ef1be2cac9 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Fri, 21 Jan 2022 21:48:16 +0100 Subject: [PATCH 28/42] Revert "Removed JCenter repository incl. related search." This reverts commit f5fa41bd4340498914991e73daab1b8a9f7a3fb2. --- .../library/maven/preset/MavenPresets.java | 4 +- .../library/maven/search/JcenterSearch.java | 114 ++++++++++++++++++ .../library/maven/search/SearchService.java | 15 ++- 3 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java index b36a9dbe3..a4a97fae6 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Gluon and/or its affiliates. + * Copyright (c) 2016, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -38,12 +38,14 @@ public class MavenPresets { public static final String MAVEN = "Maven Central"; + public static final String JCENTER = "Jcenter"; public static final String SONATYPE = "Sonatype"; public static final String GLUON_NEXUS = "Gluon Nexus"; public static final String LOCAL = "Local"; private static final List REPOSITORIES = Arrays.asList( new Repository(MAVEN, "default", "https://repo1.maven.org/maven2/"), + new Repository(JCENTER, "default", "https://jcenter.bintray.com"), new Repository(SONATYPE + " (snapshots)", "default", "https://oss.sonatype.org/content/repositories/snapshots"), new Repository(SONATYPE + " (releases)", "default", "https://oss.sonatype.org/content/repositories/releases"), new Repository(GLUON_NEXUS + " (releases)", "default", "https://nexus.gluonhq.com/nexus/content/repositories/releases")); diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java new file mode 100644 index 000000000..0822d653c --- /dev/null +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, Gluon and/or its affiliates. + * All rights reserved. Use is subject to license terms. + * + * This file is available and licensed under the following license: + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the distribution. + * - Neither the name of Oracle Corporation and Gluon nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.search; + +import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.preset.MavenPresets; +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonReader; +import static org.apache.commons.codec.binary.Base64.encodeBase64; +import org.apache.http.HttpResponse; +import org.apache.http.client.HttpClient; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.HttpClients; +import org.eclipse.aether.artifact.DefaultArtifact; + +public class JcenterSearch implements Search { + + // bintray + + // This requires authentication: +// private static final String URL_PREFIX = "https://api.bintray.com/search/packages?name="; + // This doesn't require authentication, limited to 50 results: + private static final String URL_PREFIX = "https://api.bintray.com/search/packages/maven?q=*"; + private static final String URL_SUFFIX = "*"; + + private final HttpClient client; + private final String username; + private final String password; + + public JcenterSearch(String username, String password) { + client = HttpClients.createDefault(); + this.username = username; + this.password = password; + } + + @Override + public List getCoordinates(String query) { + final Map map = new HashMap<>(); + map.put("Repository", MavenPresets.JCENTER); + + try { + HttpGet request = new HttpGet(URL_PREFIX + query + URL_SUFFIX); + if (!username.isEmpty() && !password.isEmpty()) { + String authStringEnc = new String(encodeBase64((username + ":" + password).getBytes())); + request.addHeader("Authorization", "Basic " + authStringEnc); + } + request.setHeader("Accept", "application/json"); + HttpResponse response = client.execute(request); + try (JsonReader rdr = Json.createReader(response.getEntity().getContent())) { + JsonArray obj = rdr.readArray(); + if (obj != null && !obj.isEmpty()) { + return obj.getValuesAs(JsonObject.class) + .stream() + .map(o -> { + JsonArray ids = o.getJsonArray("system_ids"); + if (ids != null && !ids.isEmpty()) { + return ids.stream() + .map(j -> j.toString().replaceAll("\"", "") + ":" + MIN_VERSION) + .collect(Collectors.toList()); + } + return null; + }) + .filter(Objects::nonNull) + .flatMap(l -> l.stream()) + .distinct() + .map(gav -> new DefaultArtifact(gav, map)) + .collect(Collectors.toList()); + } + } + } catch (IOException ex) { + Logger.getLogger(JcenterSearch.class.getName()).log(Level.SEVERE, null, ex); + } + return null; + } + +} diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java index b9ffc29e7..0437fd7bc 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2022, Gluon and/or its affiliates. + * Copyright (c) 2016, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -111,6 +111,7 @@ protected Void call() throws Exception { tasks = Arrays.asList( createSearchTask(new MavenSearch()), createSearchTask(new NexusSearch(MavenPresets.SONATYPE, "http://oss.sonatype.org", "", "")), + createSearchTask(new JcenterSearch("", "")), createSearchTask(new NexusSearch(MavenPresets.GLUON_NEXUS, "https://nexus.gluonhq.com/nexus", "", "")), createSearchTask(new LocalSearch(userM2Repository))); @@ -176,6 +177,18 @@ private List getLatestVersions(Map getAllVersions(Map> mapArtifacts) { + List list = new ArrayList<>(); + mapArtifacts.forEach((s, l) -> { + l.stream() + .filter(a -> !a.getVersion().toLowerCase(Locale.ROOT).contains("javadoc")) + .filter(a -> !a.getVersion().toLowerCase(Locale.ROOT).contains("source")) + .forEach(list::add); + }); + return list; + } + private Version getVersion(String version) { Version v1 = null; try { From e14bde067705edc3652fb004f56a07305f376c40 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sat, 22 Jan 2022 21:31:11 +0100 Subject: [PATCH 29/42] KIT continues to work with Java-11 --- pom.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 60265b6af..00532d14a 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 1.1.0 6.1.0 4.0.13 - 17 + 11 UTF-8 @@ -57,7 +57,6 @@ 3.8.1 false - ${maven.compiler.release} From ee822831ddbfda10e06d2e8f8024c4a816fab717 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sat, 22 Jan 2022 21:31:33 +0100 Subject: [PATCH 30/42] APP runs with Java-17 --- app/pom.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/pom.xml b/app/pom.xml index 4837766a4..1a3eddc38 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -16,6 +16,7 @@ ${maven.build.timestamp} ${java.version}, ${java.runtime.name} ${javafx.version} + 17 yyyy-MM-dd HH:mm:ss @@ -42,6 +43,14 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + false + ${maven.compiler.release} + + org.apache.maven.plugins maven-shade-plugin From 9ec25260fb8d8cf6ba0f1e5f569cfc4be7101949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Sat, 22 Jan 2022 23:32:22 +0100 Subject: [PATCH 31/42] Introduced background library import using a JavaFX task. --- .../app/AppPlatformDirectories.java | 19 +++ .../scenebuilder/app/SceneBuilderApp.java | 11 +- .../app/library/user/UserLibraryImporter.java | 151 ++++++++++++++---- .../app/preferences/PreferencesImporter.java | 1 + .../library/user/UserLibraryImporterTest.java | 72 ++++++--- 5 files changed, 198 insertions(+), 56 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java index bcb084377..134da33c7 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatformDirectories.java @@ -31,7 +31,11 @@ */ package com.oracle.javafx.scenebuilder.app; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.logging.Level; +import java.util.logging.Logger; public interface AppPlatformDirectories { @@ -85,6 +89,21 @@ public interface AppPlatformDirectories { */ Path getLogFolder(); + /** + * Creates the user library folder when needed. + * If the directory cannot be created, the error is logged. + */ + public default void createUserLibraryFolder() { + Path libDir = getUserLibraryFolder(); + if (Files.notExists(libDir)) { + try { + Files.createDirectories(libDir); + } catch (IOException e) { + Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to create user library directory!", e); + } + } + } + /* MessageBox folder is not provided by this as MessageBox folder previously * was package private, here all previously public directories are accessible. */ diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index 4e62a2c96..eb177c355 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -372,11 +372,8 @@ public void handleLaunch(List files) { setApplicationUncaughtExceptionHandler(); PreferencesImporter prefsImporter = PreferencesController.getSingleton().getImporter(); prefsImporter.askForActionAndRun(); - - UserLibraryImporter userLibImporter = PreferencesController.getSingleton().getUserLibraryImporter(); - userLibImporter.performImportWhenDesired(); - - // Creates the user library + + AppPlatform.getAppDirectories().createUserLibraryFolder(); MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); String userLibDir = AppPlatform.getAppDirectories().getUserLibraryFolder().normalize().toString(); userLibrary = new UserLibrary(userLibDir, @@ -416,6 +413,10 @@ public void handleLaunch(List files) { userLibrary.startWatching(); + // The import will start as soon as the user library is initialized. + // Import status, if triggered, if failed or completed, will be written to log. + Platform.runLater(UserLibraryImporter.createImportTask()); + sendTrackingStartupInfo(); if (showWelcomeDialog) { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index ba067479d..ec98a2cfb 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -48,35 +48,39 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.AppPlatform; +import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; +import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; import com.oracle.javafx.scenebuilder.app.util.AppSettings; -public class UserLibraryImporter { +import javafx.concurrent.Task; +import javafx.concurrent.WorkerStateEvent; + +public final class UserLibraryImporter { protected static final String USER_LIBRARY_IMPORT_COMPLETE = "-userlib-import-done"; private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; private final Preferences preferences; private final AppPlatformDirectories appDirectories; - + public UserLibraryImporter(Preferences applicationPreferences) { this(AppPlatform.getAppDirectories(), applicationPreferences); } - + public UserLibraryImporter(AppPlatformDirectories directories, Preferences applicationPreferences) { this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), directories, applicationPreferences); } - + public UserLibraryImporter(Optional appVersion, AppPlatformDirectories appDirectories, Preferences applicationPreferences) { this.version = Objects.requireNonNull(appVersion); this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); } - protected AppPlatformDirectories getPlatformDirectories() { + AppPlatformDirectories getPlatformDirectories() { return this.appDirectories; } @@ -86,27 +90,30 @@ protected AppPlatformDirectories getPlatformDirectories() { * or b) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -no-import} * or c) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -userlib-import-done} * Of course, the import also takes only place if a previous version library folder is found. + * + * @param timestamp {@link LocalDateTime} when the import is taking place + * @return {@link ImportResult} describing how the activity was completed. */ - public void performImportWhenDesired() { - performImportWhenDesired(LocalDateTime.now()); - } - - protected void performImportWhenDesired(LocalDateTime timestamp) { + ImportResult performImportWhenDesired(LocalDateTime timestamp) { Objects.requireNonNull(timestamp); String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); if (importDecision.contains(PreferencesImporter.DECISION_NO_IMPORT)) { logger.log(Level.INFO, "Previous version user library will not be imported as user opted out."); - return; + return ImportResult.SKIPPED; } if (importDecision.contains(USER_LIBRARY_IMPORT_COMPLETE)) { logger.log(Level.INFO, "User library already imported."); - return; + return ImportResult.ALREADY_COMPLETED; } - importPreviousVersionUserLibrary(timestamp); + return importPreviousVersionUserLibrary(timestamp); } - protected void importPreviousVersionUserLibrary(LocalDateTime timestamp) { + void clearImportDecision() { + preferences.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); + } + + ImportResult importPreviousVersionUserLibrary(LocalDateTime timestamp) { String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()); String importStatus = importDecision+USER_LIBRARY_IMPORT_COMPLETE; preferences.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,importStatus); @@ -115,12 +122,14 @@ protected void importPreviousVersionUserLibrary(LocalDateTime timestamp) { Optional oldSettingsDirectory = previousVersionUserLibraryPath(candidates); if (oldSettingsDirectory.isPresent()) { Path userLib = appDirectories.getUserLibraryFolder(); - importUserLibraryContentsFrom(oldSettingsDirectory.get(), userLib); + return importUserLibraryContentsFrom(oldSettingsDirectory.get(), userLib); } + return ImportResult.NOTHING_TO_IMPORT; } - - private void importUserLibraryContentsFrom(Path sourceSettings, Path actualLibraryDir) { + + private ImportResult importUserLibraryContentsFrom(Path sourceSettings, Path actualLibraryDir) { Path oldLibrary = sourceSettings.resolve("Library"); + ImportResult status = ImportResult.COMPLETED; if (Files.exists(oldLibrary) && Files.isDirectory(oldLibrary)) { logger.log(Level.INFO, oldLibrary.toAbsolutePath().toString()); try { @@ -129,21 +138,24 @@ private void importUserLibraryContentsFrom(Path sourceSettings, Path actualLibra String template = "Failed to import user library from %s"; String message = String.format(template, oldLibrary); logger.log(Level.SEVERE, message, e); + status = ImportResult.COMPLETED_WITH_ERRORS; } } + return status; } - protected void copyDirectory(Path sourceDir, Path destinationDir) throws IOException { - Files.walk(sourceDir).forEach(item -> adjustDestinationPathAndTryCopy(sourceDir, destinationDir, item)); + void copyDirectory(Path sourceDir, Path destinationDir) throws IOException { + Files.walk(sourceDir).forEach(item -> + adjustDestinationPathAndTryCopy(sourceDir, destinationDir, item)); } - protected void adjustDestinationPathAndTryCopy(Path sourceDir, Path destinationDir, Path source) { + void adjustDestinationPathAndTryCopy(Path sourceDir, Path destinationDir, Path source) { Path relativeSrc = sourceDir.relativize(source); Path destination = destinationDir.resolve(relativeSrc).toAbsolutePath(); tryFileCopy(source, destination); } - protected void tryFileCopy(Path source, Path destination) { + void tryFileCopy(Path source, Path destination) { try { createDirectoryIfNeeded(destination); copyFile(source, destination); @@ -152,14 +164,14 @@ protected void tryFileCopy(Path source, Path destination) { } } - protected void copyFile(Path source, Path destination) throws IOException { + void copyFile(Path source, Path destination) throws IOException { if (!Files.isDirectory(source)) { logger.log(Level.INFO, "Importing {0} into {1}", new Object[] { source, destination }); Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); } } - protected void createDirectoryIfNeeded(Path destination) throws IOException { + void createDirectoryIfNeeded(Path destination) throws IOException { if (Files.isDirectory(destination) && Files.notExists(destination)) { Files.createDirectories(destination); } @@ -169,12 +181,13 @@ private List collectLibraryCandidates(Path appData) { try (Stream files = Files.list(appData)) { return files.filter(Files::isDirectory).collect(Collectors.toList()); } catch (IOException e) { - logger.log(Level.SEVERE, "Error while searching previous version user library locations.", e); + logger.log(Level.SEVERE, + "Error while searching previous version user library locations.", e); } return Collections.emptyList(); } - - protected Optional previousVersionUserLibraryPath(List candidates) { + + Optional previousVersionUserLibraryPath(List candidates) { Objects.requireNonNull(candidates, "list of path candidates must not be null"); Map previousVersionLibDirs = collectPreviousVersionLibraryDirs(candidates); return previousVersionLibDirs.entrySet().stream() @@ -182,8 +195,8 @@ protected Optional previousVersionUserLibraryPath(List candidates) { .map(e -> e.getValue()) .findFirst(); } - - protected Map collectPreviousVersionLibraryDirs(List candidates) { + + Map collectPreviousVersionLibraryDirs(List candidates) { if (version.isEmpty()) { return Collections.emptyMap(); } @@ -211,4 +224,84 @@ protected Map collectPreviousVersionLibraryDirs(List can return sceneBuilderDirs; } + + static class UserLibraryImportTask extends Task { + + private final Logger logger = Logger.getLogger(UserLibraryImportTask.class.getName()); + private final LocalDateTime timestamp; + private final UserLibraryImporter userLibImporter; + + UserLibraryImportTask(LocalDateTime timestamp, UserLibraryImporter userLibImporter) { + this.timestamp = timestamp; + this.userLibImporter = userLibImporter; + + setOnFailed(this::logException); + setOnSucceeded(this::logSuccess); + } + + @Override + protected ImportResult call() throws Exception { + return userLibImporter.performImportWhenDesired(timestamp); + } + + private void logException(WorkerStateEvent event) { + if (getException() != null) { + logger.log(Level.SEVERE, + "Import of User Library failed with error!", getException()); + } else { + logger.log(Level.SEVERE, + "Import of User Library failed with error!"); + } + } + + private void logSuccess(WorkerStateEvent event) { + logger.log(Level.SEVERE, "User Library Import finished with: {0}", getValue()); + } + + LocalDateTime getTimestamp() { + return timestamp; + } + + UserLibraryImporter getUserLibImporter() { + return userLibImporter; + } + } + + public static Task createImportTask() { + return new UserLibraryImportTask(LocalDateTime.now(), + PreferencesController.getSingleton() + .getUserLibraryImporter()); + } + + public enum ImportResult { + /** + * User library was successfully imported. + */ + COMPLETED, + + /** + * User library was previously successfully imported. + */ + ALREADY_COMPLETED, + + /** + * User library import failed with an error. + */ + FAILED, + + /** + * User library import was skipped as user opted out. + */ + SKIPPED, + + /** + * No older user library directory found so the import was skipped. + */ + NOTHING_TO_IMPORT, + + /** + * User library was imported but there were errors importing individual JARs. + */ + COMPLETED_WITH_ERRORS; + } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index a5a4af42e..50b01b9ac 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -134,6 +134,7 @@ private void copyChildren(Preferences source, Preferences targetNode) throws Bac */ public boolean askForImport() { String lastTimeAsked = target.get(PREF_ASKED_FOR_IMPORT, null); + return lastTimeAsked == null; } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index 85ee2a924..d507a18d0 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -54,11 +54,15 @@ import com.oracle.javafx.scenebuilder.app.AppPlatformDirectories; import com.oracle.javafx.scenebuilder.app.PlatformSpecificDirectories; +import com.oracle.javafx.scenebuilder.app.library.user.UserLibraryImporter.ImportResult; import com.oracle.javafx.scenebuilder.app.OperatingSystem; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; +import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; import com.oracle.javafx.scenebuilder.app.preferences.PrefsHelper; +import javafx.concurrent.Task; + public class UserLibraryImporterTest { private static Preferences testNode; @@ -115,9 +119,9 @@ public void that_user_library_paths_are_detected_for_LINUX() { Paths.get(".scenebuilder-8.5.0"), Paths.get(".scenebuilder-8.5.0-SNAPSHOT"), Paths.get("Scene Builder-19.0.1")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get(".scenebuilder-15.0.0"), x.get()); } @@ -130,9 +134,9 @@ public void that_legacy_library_path_is_detected_for_LINUX() { Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), Paths.get(".scenebuilder")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get(".scenebuilder"), x.get()); } @@ -147,9 +151,9 @@ public void that_user_library_paths_are_detected_for_WINDOWS() { Paths.get("Scene Builder-8.5.0"), Paths.get("Scene Builder-19.0.1"), Paths.get(".scenebuilder")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); } @@ -162,9 +166,9 @@ public void that_legacy_library_path_is_detected_for_WINDOWS() { Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), Paths.get(".scenebuilder")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder"), x.get()); } @@ -206,15 +210,16 @@ public void that_import_is_skipped_when_user_opted_out() { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()+PreferencesImporter.DECISION_NO_IMPORT); - + String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - classUnderTest.performImportWhenDesired(timestamp); - + ImportResult result = classUnderTest.performImportWhenDesired(timestamp); + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); - + + assertEquals(ImportResult.SKIPPED, result); assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-no-import", documentedResult); assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); @@ -224,15 +229,16 @@ public void that_import_is_skipped_when_user_opted_out() { public void that_import_is_skipped_when_done_before() { LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,timestamp.toString()+UserLibraryImporter.USER_LIBRARY_IMPORT_COMPLETE); - + String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - classUnderTest.performImportWhenDesired(timestamp); - + ImportResult result = classUnderTest.performImportWhenDesired(timestamp); + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); - + + assertEquals(ImportResult.ALREADY_COMPLETED, result); assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); @@ -256,15 +262,16 @@ public void that_old_files_are_imported() throws Exception { Path userLib = appDirectories.getUserLibraryFolder(); Files.createDirectories(userLib); - + classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - classUnderTest.performImportWhenDesired(timestamp); - + ImportResult result = classUnderTest.performImportWhenDesired(timestamp); + String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); - + + assertEquals(ImportResult.COMPLETED, result); assertNotNull(documentedResult); assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); - + File[] importedFiles = appDirectories.getUserLibraryFolder().toFile().listFiles(); assertEquals(1, importedFiles.length); assertEquals("MyCustomArtifact.txt", importedFiles[0].getName()); @@ -272,13 +279,25 @@ public void that_old_files_are_imported() throws Exception { @Test public void that_correct_AppDirectories_are_used_by_default() { - if (testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null) !=null) { + if (testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null) !=null) { testNode.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); } classUnderTest = new UserLibraryImporter(testNode); assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); } + @Test + public void that_import_task_is_created() { + Task task = UserLibraryImporter.createImportTask(); + + assertTrue(task instanceof UserLibraryImporter.UserLibraryImportTask); + + UserLibraryImporter.UserLibraryImportTask specialTask = (UserLibraryImporter.UserLibraryImportTask) task; + + assertNotNull(specialTask.getTimestamp()); + assertNotNull(specialTask.getUserLibImporter()); + } + private static AppPlatformDirectories forLinux(String version) { return new PlatformSpecificDirectories(OperatingSystem.LINUX, version); } @@ -290,4 +309,13 @@ private static AppPlatformDirectories forMacOS(String version) { private static AppPlatformDirectories forWindows(String version) { return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); } + + /** + * This is only for debugging so that the import decision can be removed if needed + */ + public static void main(String[] args) { + PreferencesController.getSingleton() + .getUserLibraryImporter() + .clearImportDecision(); + } } From 0c98a2009ba54f877927c940ec4e8a9b6c989214 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 23 Jan 2022 18:47:46 +0100 Subject: [PATCH 32/42] Working draft for preferences and user library import. User library is imported using a task, so it runs in the background. Progress and results are logged. --- app/pom.xml | 2 +- .../javafx/scenebuilder/app/AppPlatform.java | 3 +- .../scenebuilder/app/SceneBuilderApp.java | 34 ++---- .../app/library/user/UserLibraryImporter.java | 81 ++++++------- .../app/menubar/MenuBarController.java | 8 +- .../preferences/PreferencesController.java | 5 +- .../app/preferences/PreferencesImporter.java | 100 ++++++++------- .../VersionedPreferencesFinder.java | 2 + .../RegistrationWindowController.java | 6 +- .../app/i18n/SceneBuilderApp.properties | 1 + .../scenebuilder/app/menubar/MenuBar.fxml | 1 + .../library/user/UserLibraryImporterTest.java | 57 +++------ .../PreferencesControllerTest.java | 37 +++--- .../preferences/PreferencesImporterTest.java | 75 ++++++------ .../app/preferences/PrefsHelper.java | 17 ++- .../VersionedPreferencesFinderTest.java | 5 +- docs/snippets/AppConfiguration.md | 45 ++++--- .../library/maven/preset/MavenPresets.java | 4 +- .../library/maven/search/JcenterSearch.java | 114 ------------------ .../library/maven/search/SearchService.java | 3 +- pom.xml | 1 + 21 files changed, 240 insertions(+), 361 deletions(-) delete mode 100644 kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java diff --git a/app/pom.xml b/app/pom.xml index 1a3eddc38..7bb5f83c8 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -16,8 +16,8 @@ ${maven.build.timestamp} ${java.version}, ${java.runtime.name} ${javafx.version} - 17 yyyy-MM-dd HH:mm:ss + 17 diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java index a11c124a6..6868a1ae9 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/AppPlatform.java @@ -60,8 +60,7 @@ public static synchronized AppPlatformDirectories getAppDirectories() { assert platformSpecificDirectories != null; return platformSpecificDirectories; } - - + /* * TODO: * How to deal with snapshot versions? diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index eb177c355..d927eb422 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -370,8 +370,9 @@ public void start(Stage stage) throws Exception { public void handleLaunch(List files) { boolean showWelcomeDialog = files.isEmpty(); setApplicationUncaughtExceptionHandler(); + PreferencesImporter prefsImporter = PreferencesController.getSingleton().getImporter(); - prefsImporter.askForActionAndRun(); + boolean doImport = prefsImporter.askForActionAndRun(); AppPlatform.getAppDirectories().createUserLibraryFolder(); MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); @@ -415,7 +416,9 @@ public void handleLaunch(List files) { // The import will start as soon as the user library is initialized. // Import status, if triggered, if failed or completed, will be written to log. - Platform.runLater(UserLibraryImporter.createImportTask()); + if (doImport) { + Platform.runLater(UserLibraryImporter.createImportTask()); + } sendTrackingStartupInfo(); @@ -432,13 +435,7 @@ public void handleLaunch(List files) { } WelcomeDialogWindowController.getInstance().getStage().setOnHidden(event -> { - showUpdateDialogIfRequired(newWindow, () -> { - if (!Platform.isFxApplicationThread()) { - Platform.runLater(() -> showRegistrationDialogIfRequired(newWindow)); - } else { - showRegistrationDialogIfRequired(newWindow); - } - }); + showUpdateDialogIfRequired(newWindow); }); // Unless we're on a Mac we're starting SB directly (fresh start) @@ -908,7 +905,7 @@ private void userLibraryExplorationCountDidChange() { } } - private void showUpdateDialogIfRequired(DocumentWindowController dwc, Runnable runAfterUpdateDialog) { + private void showUpdateDialogIfRequired(DocumentWindowController dwc) { AppSettings.getLatestVersion(latestVersion -> { if (latestVersion == null) { // This can be because the url was not reachable so we don't show the update dialog. @@ -939,11 +936,8 @@ private void showUpdateDialogIfRequired(DocumentWindowController dwc, Runnable r Platform.runLater(() -> { UpdateSceneBuilderDialog dialog = new UpdateSceneBuilderDialog(latestVersion, latestVersionText, latestVersionAnnouncementURL, dwc.getStage()); - dialog.setOnHidden(event -> runAfterUpdateDialog.run()); dialog.showAndWait(); }); - } else { - runAfterUpdateDialog.run(); } } catch (NumberFormatException ex) { Platform.runLater(() -> showVersionNumberFormatError(dwc)); @@ -1011,20 +1005,6 @@ private boolean isUpdateDialogDateReached(PreferencesRecordGlobal recordGlobal) } } - private void showRegistrationDialogIfRequired(DocumentWindowController dwc) { - PreferencesController pc = PreferencesController.getSingleton(); - PreferencesRecordGlobal recordGlobal = pc.getRecordGlobal(); - String registrationHash = recordGlobal.getRegistrationHash(); - if (registrationHash == null) { - performControlAction(ApplicationControlAction.REGISTER, dwc); - } else { - String registrationEmail = recordGlobal.getRegistrationEmail(); - if (registrationEmail == null && Math.random() > 0.8) { - performControlAction(ApplicationControlAction.REGISTER, dwc); - } - } - } - private void logInfoMessage(String key) { for (DocumentWindowController dwc : windowList) { dwc.getEditorController().getMessageLog().logInfoMessage(key, I18N.getBundle()); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index ec98a2cfb..347f6461b 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -35,7 +35,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; -import java.time.LocalDateTime; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -59,7 +58,8 @@ import javafx.concurrent.WorkerStateEvent; public final class UserLibraryImporter { - protected static final String USER_LIBRARY_IMPORT_COMPLETE = "-userlib-import-done"; + + protected static final String PREF_IMPORT_USER_LIBRARY = "IMPORT_USER_LIBRARY"; private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; @@ -85,38 +85,38 @@ AppPlatformDirectories getPlatformDirectories() { } /** - * The library import is only performed when following condition is met: - * a) the key {@code ASKED_FOR_IMPORT} does not exist in application preferences - * or b) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -no-import} - * or c) the value of {@code ASKED_FOR_IMPORT} does not contain {@code -userlib-import-done} - * Of course, the import also takes only place if a previous version library folder is found. - * - * @param timestamp {@link LocalDateTime} when the import is taking place + * The library import is only performed when the property key + * {@code IMPORT_USER_LIBRARY} does not exist or is defined as "true". + * * @return {@link ImportResult} describing how the activity was completed. */ - ImportResult performImportWhenDesired(LocalDateTime timestamp) { - Objects.requireNonNull(timestamp); - String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); - if (importDecision.contains(PreferencesImporter.DECISION_NO_IMPORT)) { - logger.log(Level.INFO, "Previous version user library will not be imported as user opted out."); - return ImportResult.SKIPPED; - } - - if (importDecision.contains(USER_LIBRARY_IMPORT_COMPLETE)) { - logger.log(Level.INFO, "User library already imported."); - return ImportResult.ALREADY_COMPLETED; + ImportResult performImportWhenDesired() { + boolean forcedImport = importForced(); + boolean requiresImport = preferences.getBoolean(PREF_IMPORT_USER_LIBRARY, true); + if (requiresImport || forcedImport) { + if (forcedImport) { + logger.log(Level.FINE, "detected -DforceImport=true"); + } + return importPreviousVersionUserLibrary(); } - return importPreviousVersionUserLibrary(timestamp); + logger.log(Level.INFO, "Previous version user library will not be imported."); + return ImportResult.SKIPPED; + } + + private boolean importForced() { + String forceImport = System.getProperty("forceImport", "false"); + return "true".equalsIgnoreCase(forceImport); } void clearImportDecision() { - preferences.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); + preferences.remove(PREF_IMPORT_USER_LIBRARY); + } + + private void documentThatImportIsDone() { + preferences.putBoolean(PREF_IMPORT_USER_LIBRARY, false); } - ImportResult importPreviousVersionUserLibrary(LocalDateTime timestamp) { - String importDecision = preferences.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, timestamp.toString()); - String importStatus = importDecision+USER_LIBRARY_IMPORT_COMPLETE; - preferences.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,importStatus); + ImportResult importPreviousVersionUserLibrary() { Path appData = appDirectories.getApplicationDataRoot(); List candidates = collectLibraryCandidates(appData); Optional oldSettingsDirectory = previousVersionUserLibraryPath(candidates); @@ -141,6 +141,7 @@ private ImportResult importUserLibraryContentsFrom(Path sourceSettings, Path act status = ImportResult.COMPLETED_WITH_ERRORS; } } + documentThatImportIsDone(); return status; } @@ -224,24 +225,21 @@ Map collectPreviousVersionLibraryDirs(List candidates) { return sceneBuilderDirs; } - + static class UserLibraryImportTask extends Task { private final Logger logger = Logger.getLogger(UserLibraryImportTask.class.getName()); - private final LocalDateTime timestamp; private final UserLibraryImporter userLibImporter; - - UserLibraryImportTask(LocalDateTime timestamp, UserLibraryImporter userLibImporter) { - this.timestamp = timestamp; + + UserLibraryImportTask(UserLibraryImporter userLibImporter) { this.userLibImporter = userLibImporter; - setOnFailed(this::logException); setOnSucceeded(this::logSuccess); } @Override protected ImportResult call() throws Exception { - return userLibImporter.performImportWhenDesired(timestamp); + return userLibImporter.performImportWhenDesired(); } private void logException(WorkerStateEvent event) { @@ -258,37 +256,28 @@ private void logSuccess(WorkerStateEvent event) { logger.log(Level.SEVERE, "User Library Import finished with: {0}", getValue()); } - LocalDateTime getTimestamp() { - return timestamp; - } - UserLibraryImporter getUserLibImporter() { return userLibImporter; } } public static Task createImportTask() { - return new UserLibraryImportTask(LocalDateTime.now(), - PreferencesController.getSingleton() + return new UserLibraryImportTask(PreferencesController.getSingleton() .getUserLibraryImporter()); } - public enum ImportResult { + enum ImportResult { /** * User library was successfully imported. */ COMPLETED, - - /** - * User library was previously successfully imported. - */ - ALREADY_COMPLETED, + /** * User library import failed with an error. */ FAILED, - + /** * User library import was skipped as user opted out. */ diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/MenuBarController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/MenuBarController.java index 16a11dced..0b86a842c 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/MenuBarController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/menubar/MenuBarController.java @@ -408,6 +408,8 @@ public class MenuBarController { @FXML private MenuItem checkUpdatesMenuItem; @FXML + private MenuItem registerMenuItem; + @FXML private MenuItem sceneBuilderHomeMenuItem; @FXML private MenuItem gettingStartedMenuItem; @@ -480,7 +482,7 @@ public boolean isDebugMenuVisible() { } return result; } - + public static synchronized MenuBarController getSystemMenuBarController() { if (systemMenuBarController == null) { @@ -628,6 +630,7 @@ private void controllerDidLoadFxml() { assert helpMenuItem != null; assert aboutMenuItem != null; assert checkUpdatesMenuItem != null; + assert registerMenuItem != null; assert gettingStartedMenuItem != null; assert apiDocMenuItem != null; assert cssReferenceGuideMenuItem != null; @@ -1135,6 +1138,7 @@ public String getTitle() { helpMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.HELP)); helpMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F1)); checkUpdatesMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.CHECK_UPDATES)); + registerMenuItem.setUserData(new ApplicationControlActionController(ApplicationControlAction.REGISTER)); gettingStartedMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.HELP_OPEN_GETTING_STARTED_GUIDE)); gettingStartedMenuItem.setAccelerator(new KeyCodeCombination(KeyCode.F2)); apiDocMenuItem.setUserData(new DocumentControlActionController(DocumentControlAction.HELP_OPEN_OPENJFX_APIDOC)); @@ -1904,7 +1908,7 @@ public void perform() { } } - + class ZoomOutActionController extends MenuItemController { diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java index fcd9a28db..cbe5cad07 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesController.java @@ -159,10 +159,11 @@ public static synchronized PreferencesController getSingleton() { * The custom node is only set with the first call, when the {@link PreferencesController} was initialized before, t * the Preferences node to be used cannot be changed anymore. * + * @treatAsPrivate * @param prefs {@link Preferences} node to be used, can be null. * @return The one and only PreferencesController instance. */ - protected static synchronized PreferencesController getSingleton(Preferences prefs) { + static synchronized PreferencesController getSingleton(Preferences prefs) { if (singleton == null) { singleton = new PreferencesController(prefs); singleton.getRecordGlobal().readFromJavaPreferences(); @@ -250,6 +251,6 @@ public PreferencesImporter getImporter() { } public UserLibraryImporter getUserLibraryImporter() { - return new UserLibraryImporter(applicationPreferences); + return new UserLibraryImporter(applicationRootPreferences); } } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 50b01b9ac..cb3eb9046 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -31,7 +31,6 @@ */ package com.oracle.javafx.scenebuilder.app.preferences; -import java.time.LocalDateTime; import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; @@ -50,25 +49,23 @@ * Imports all keys and children (including keys) from an arbitrary * {@link Preferences} node into the predefined applicationPreferences node. */ -public class PreferencesImporter { +public final class PreferencesImporter { /*************************************************************************** * * * Static fields * * * **************************************************************************/ - + // GLOBAL PREFERENCES - public static final String PREF_ASKED_FOR_IMPORT = "ASKED_FOR_IMPORT"; - - public static final String DECISION_NO_IMPORT = "-no-import"; - + public static final String PREF_PERFORM_IMPORT = "PERFORM_IMPORT"; + /*************************************************************************** * * * Instance fields * * * **************************************************************************/ - + private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Preferences target; private final Optional optionalSourceNode; @@ -100,17 +97,17 @@ public PreferencesImporter(Preferences applicationPreferences, Optional> alertInteraction = () -> { SBAlert customAlert = new SBAlert(AlertType.CONFIRMATION, ButtonType.YES, ButtonType.NO); - customAlert.initOwner(null); + customAlert.setTitle("Gluon Scene Builder"); customAlert.setHeaderText("Import settings"); - customAlert.setContentText("Previous version settings found.\nDo you want to import those?\n\nScene Builder will remember your decision and not ask again."); + customAlert.setContentText( + "Previous version settings found. Do you want to import those?" + + "\nScene Builder will also import the JAR files from your library." + + "\n\nYour decision will be remembered so you won't be asked again."); return customAlert.showAndWait(); }; - askForActionAndRun(alertInteraction); + return askForActionAndRun(alertInteraction); } - - protected void askForActionAndRun(Supplier> alertInteraction) { + + boolean askForActionAndRun(Supplier> alertInteraction) { if (askForImportIfOlderSettingsExist()) { - executeInteractionAndImport(alertInteraction); + return executeInteractionAndImport(alertInteraction); } + return false; } - - protected void executeInteractionAndImport(Supplier> alertInteraction) { + + boolean executeInteractionAndImport(Supplier> alertInteraction) { Optional response = alertInteraction.get(); if (response.isPresent() && ButtonType.YES.equals(response.get())) { logger.log(Level.INFO, "User decided to import previous version settings."); tryImportingPreviousVersionSettings(); + return true; } else { documentThatNoImportIsDesired(); } + return false; } + } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java index 4d2cb8402..686a94ce7 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinder.java @@ -119,6 +119,8 @@ public List getPreviousVersions() { public Optional previousVersionPrefs() { List previousVersions = getPreviousVersions(); if (previousVersions.isEmpty()) { + Logger.getLogger(VersionedPreferencesFinder.class.getName()).log(Level.FINE, + "No previous versions preferences found!"); return Optional.empty(); } return Optional.of(previousVersions.get(0)); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/registration/RegistrationWindowController.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/registration/RegistrationWindowController.java index 547d9a128..0c9a84948 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/registration/RegistrationWindowController.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/registration/RegistrationWindowController.java @@ -1,6 +1,6 @@ /* - * Copyright (c) 2016, Gluon and/or its affiliates. - * Copyright (c) 2012, 2014, Oracle and/or its affiliates, 2016, Gluon. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. + * Copyright (c) 2012, 2014, Oracle and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -163,6 +163,7 @@ private String getUniqueId(){ } } } catch (UnknownHostException | SocketException e) { + // Intentionally blank catch } if (uniqueId.isEmpty()) { @@ -188,6 +189,7 @@ private String computeHash(byte[] buffer) { return hexStr; } catch (NoSuchAlgorithmException e) { + // Intentionally blank catch } return ""; 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 81225e5af..05b4f0973 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 @@ -192,6 +192,7 @@ menu.title.no.window = No Window menu.title.scene.builder.help = Scene Builder Help menu.title.check.updates = Check for Update... menu.title.about = About Scene Builder +menu.title.register = Register... menu.title.help.javafx=JavaFX menu.title.help.openjfx.getting.started=Getting started with JavaFX menu.title.help.openjfx.api.docs=JavaFX API Documentation diff --git a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/menubar/MenuBar.fxml b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/menubar/MenuBar.fxml index b3b87e2c8..bdf6a39ed 100644 --- a/app/src/main/resources/com/oracle/javafx/scenebuilder/app/menubar/MenuBar.fxml +++ b/app/src/main/resources/com/oracle/javafx/scenebuilder/app/menubar/MenuBar.fxml @@ -314,6 +314,7 @@ + diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java index d507a18d0..0f191e1ec 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporterTest.java @@ -43,7 +43,6 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; -import java.time.LocalDateTime; import java.util.List; import java.util.Optional; import java.util.prefs.Preferences; @@ -58,7 +57,6 @@ import com.oracle.javafx.scenebuilder.app.OperatingSystem; import com.oracle.javafx.scenebuilder.app.preferences.AppVersion; import com.oracle.javafx.scenebuilder.app.preferences.PreferencesController; -import com.oracle.javafx.scenebuilder.app.preferences.PreferencesImporter; import com.oracle.javafx.scenebuilder.app.preferences.PrefsHelper; import javafx.concurrent.Task; @@ -68,7 +66,7 @@ public class UserLibraryImporterTest { private static Preferences testNode; private UserLibraryImporter classUnderTest; private AppPlatformDirectories appDirectories; - + @BeforeClass public static void setup() { testNode = Preferences.userRoot() @@ -183,9 +181,9 @@ public void that_user_library_paths_are_detected_for_MACOS() { Paths.get("Scene Builder-8.5.0"), Paths.get("Scene Builder-19.0.1"), Paths.get(".scenebuilder")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder-8.5.0"), x.get()); } @@ -198,56 +196,34 @@ public void that_legacy_library_path_is_detected_for_MACOS() { Paths.get("Scene Builder"), Paths.get("Scene Builder-19.0.1"), Paths.get(".scenebuilder")); - + Optional x = classUnderTest.previousVersionUserLibraryPath(candidates); - + assertFalse(x.isEmpty()); assertEquals(Paths.get("Scene Builder"), x.get()); } @Test public void that_import_is_skipped_when_user_opted_out() { - LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); - testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT, - timestamp.toString()+PreferencesImporter.DECISION_NO_IMPORT); + testNode.putBoolean(UserLibraryImporter.PREF_IMPORT_USER_LIBRARY, false); String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - ImportResult result = classUnderTest.performImportWhenDesired(timestamp); + ImportResult result = classUnderTest.performImportWhenDesired(); - String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); + String documentedResult = testNode.get(UserLibraryImporter.PREF_IMPORT_USER_LIBRARY, null); assertEquals(ImportResult.SKIPPED, result); assertNotNull(documentedResult); - assertEquals("2022-01-05T20:14-no-import", documentedResult); - assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); - } - - @Test - public void that_import_is_skipped_when_done_before() { - LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); - testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,timestamp.toString()+UserLibraryImporter.USER_LIBRARY_IMPORT_COMPLETE); - - String version = "17.0.0"; - OperatingSystem os = OperatingSystem.LINUX; - AppPlatformDirectories appDirectories = new TestAppDirectories(os, version); - classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - ImportResult result = classUnderTest.performImportWhenDesired(timestamp); - - String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); - - assertEquals(ImportResult.ALREADY_COMPLETED, result); - assertNotNull(documentedResult); - assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); + assertEquals("false", documentedResult); assertNull(appDirectories.getUserLibraryFolder().toFile().listFiles()); } @Test public void that_old_files_are_imported() throws Exception { - LocalDateTime timestamp = LocalDateTime.of(2022, 1, 5, 20, 14); - testNode.put(PreferencesImporter.PREF_ASKED_FOR_IMPORT,timestamp.toString()); + testNode.remove(UserLibraryImporter.PREF_IMPORT_USER_LIBRARY); String version = "17.0.0"; OperatingSystem os = OperatingSystem.LINUX; @@ -264,13 +240,12 @@ public void that_old_files_are_imported() throws Exception { Files.createDirectories(userLib); classUnderTest = new UserLibraryImporter(AppVersion.fromString(version), appDirectories, testNode); - ImportResult result = classUnderTest.performImportWhenDesired(timestamp); + ImportResult result = classUnderTest.performImportWhenDesired(); - String documentedResult = testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null); + String documentedResult = testNode.get(UserLibraryImporter.PREF_IMPORT_USER_LIBRARY, ""); assertEquals(ImportResult.COMPLETED, result); - assertNotNull(documentedResult); - assertEquals("2022-01-05T20:14-userlib-import-done", documentedResult); + assertEquals("false", documentedResult); File[] importedFiles = appDirectories.getUserLibraryFolder().toFile().listFiles(); assertEquals(1, importedFiles.length); @@ -279,9 +254,6 @@ public void that_old_files_are_imported() throws Exception { @Test public void that_correct_AppDirectories_are_used_by_default() { - if (testNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null) !=null) { - testNode.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); - } classUnderTest = new UserLibraryImporter(testNode); assertTrue(classUnderTest.getPlatformDirectories() instanceof PlatformSpecificDirectories); } @@ -294,7 +266,6 @@ public void that_import_task_is_created() { UserLibraryImporter.UserLibraryImportTask specialTask = (UserLibraryImporter.UserLibraryImportTask) task; - assertNotNull(specialTask.getTimestamp()); assertNotNull(specialTask.getUserLibImporter()); } @@ -309,7 +280,7 @@ private static AppPlatformDirectories forMacOS(String version) { private static AppPlatformDirectories forWindows(String version) { return new PlatformSpecificDirectories(OperatingSystem.WINDOWS, version); } - + /** * This is only for debugging so that the import decision can be removed if needed */ diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java index bd2fe5c0e..47cc90fea 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java @@ -48,25 +48,26 @@ import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.Repository; public class PreferencesControllerTest { - + private static Preferences testNode; - private static PreferencesController classUnderTest; - + @BeforeClass public static void setup() { testNode = Preferences.userRoot() .node("PREFS_CONTROLLER_TEST") .node("com/oracle/javafx/scenebuilder/app/preferences"); + PrefsHelper.removeAllChildNodes(testNode); + PrefsHelper.setToNull(PreferencesController.getSingleton(),"singleton"); classUnderTest = PreferencesController.getSingleton(testNode); } - + @AfterClass public static void cleanup() throws Exception { Preferences.userRoot().node("PREFS_CONTROLLER_TEST").removeNode(); + PrefsHelper.setToNull(PreferencesController.getSingleton(),"singleton"); } - @Test public void that_preferences_are_stored_per_version() { @@ -83,7 +84,7 @@ public void that_prefs_root_node_is_version_specific() { String expectedPrefsNode = "/PREFS_CONTROLLER_TEST/com/oracle/javafx/scenebuilder/app/preferences/" + versionSpecificNode; assertEquals(expectedPrefsNode, prefsNodeUsed); } - + @Test public void that_most_recent_previous_version_is_detected() { PrefsHelper.removeAllNonReleaseNodes(testNode); @@ -95,7 +96,7 @@ public void that_most_recent_previous_version_is_detected() { assertEquals(new AppVersion(8, 5), mostRecentPreviousVersion.get().version()); assertEquals("SB_8.5", mostRecentPreviousVersion.get().node().name()); } - + @Test public void that_no_version_is_detected_in_case_that_only_newer_versions_exist() { PrefsHelper.removeAllNonReleaseNodes(testNode); @@ -103,15 +104,14 @@ public void that_no_version_is_detected_in_case_that_only_newer_versions_exist() Optional mostRecentPreviousVersion = classUnderTest.getPreviousVersionSettings(); assertTrue(mostRecentPreviousVersion.isEmpty()); } - + @Test public void that_previous_version_settings_can_be_imported() { PrefsHelper.removeAllNonReleaseNodes(testNode); // GIVEN - testNode.node("SB_8.9.9").put("RECENT_ITEMS", "/folder/file.fxml"); - + Preferences mavenLib = testNode.node("SB_8.9.9").node("ARTIFACTS").node("org.name:library:0.0.1"); mavenLib.put("path", "/location/of/file.jar"); mavenLib.put("groupID", "org.name"); @@ -119,37 +119,36 @@ public void that_previous_version_settings_can_be_imported() { mavenLib.put("dependencies", ""); mavenLib.put("artifactId", "library"); mavenLib.put("version", "0.0.1"); - + Preferences repository = testNode.node("SB_8.9.9").node("REPOSITORIES").node("custom-repository"); repository.put("ID", "custom-repository"); repository.put("URL", "http://localhost/myrepo"); repository.put("type", "default"); repository.put("User", "username"); repository.put("Password", "password"); - - + // WHEN PreferencesImporter importer = classUnderTest.getImporter(); importer.tryImportingPreviousVersionSettings(); - + // THEN String appVersion = AppSettings.getSceneBuilderVersion(); String versionSpecificNode = "SB_" + appVersion; Preferences targetNode = testNode.node(versionSpecificNode); - + // User Decision is memorized - assertNotNull(targetNode.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null)); - + assertNotNull(targetNode.get(PreferencesImporter.PREF_PERFORM_IMPORT, null)); + // Maven Repositories are initialized properly List artifacts = classUnderTest.getMavenPreferences().getArtifactsCoordinates(); assertFalse(artifacts.isEmpty()); assertTrue(artifacts.contains("org.name:library:0.0.1")); - + // User Repositories are properly initialized List repositories = classUnderTest.getRepositoryPreferences().getRepositories(); assertFalse(repositories.isEmpty()); assertEquals("http://localhost/myrepo", repositories.get(0).getURL()); - + // Recent items List recentItems = classUnderTest.getRecordGlobal().getRecentItems(); assertFalse(recentItems.isEmpty()); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index e106a65af..8cb254eb9 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -20,9 +20,9 @@ import javafx.scene.control.ButtonType; public class PreferencesImporterTest { - + private PreferencesImporter classUnderTest; - + @BeforeClass @AfterClass public static void cleanUpPrefs() throws Exception { @@ -34,6 +34,7 @@ public static void cleanUpPrefs() throws Exception { Preferences.userRoot().node(nodeName).removeNode(); } } + @Test public void that_settings_are_copied_between_nodes() throws Exception { Preferences mySourcePrefs = Preferences.userRoot().node("SOURCE_TO_IMPORT"); @@ -42,18 +43,18 @@ public void that_settings_are_copied_between_nodes() throws Exception { mySourcePrefs.node("CHILD1").node("CHILD2").put("key2", "value2"); mySourcePrefs.node("CHILD1").node("CHILD2").node("CHILD3").put("key3", "value3"); mySourcePrefs.node("CHILD1").node("CHILD2").node("CHILD4"); - + Preferences myTargetPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); - + classUnderTest = new PreferencesImporter(myTargetPrefs, Optional.empty()); classUnderTest.importFrom(mySourcePrefs); - + assertEquals("anyvalue", myTargetPrefs.get("anykey", null)); assertEquals("value1", myTargetPrefs.node("CHILD1").get("key1", null)); assertEquals("value2", myTargetPrefs.node("CHILD1").node("CHILD2").get("key2", null)); assertEquals("value3", myTargetPrefs.node("CHILD1").node("CHILD2").node("CHILD3").get("key3", null)); } - + @Test public void that_user_will_be_only_asked_when_import_decision_was_not_made() throws Exception { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); @@ -63,18 +64,18 @@ public void that_user_will_be_only_asked_when_import_decision_was_not_made() thr assertTrue(classUnderTest.askForImport()); - classUnderTest.saveTimestampWhenAskedForImport(); + classUnderTest.documentThatNoImportIsDesired(); assertFalse(classUnderTest.askForImport()); } - + @Test public void that_user_will_be_only_asked_when_previous_version_settings_exist() throws Exception { AppVersion oldVersion = new AppVersion(0, 9); Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); - // ensure the user was never asked before - appPrefs.remove(PreferencesImporter.PREF_ASKED_FOR_IMPORT); + // ensure the PREF_PERFORM_IMPORT setting does not exist + appPrefs.remove(PreferencesImporter.PREF_PERFORM_IMPORT); classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); assertTrue(classUnderTest.askForImportIfOlderSettingsExist()); @@ -82,31 +83,29 @@ public void that_user_will_be_only_asked_when_previous_version_settings_exist() assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); classUnderTest = new PreferencesImporter(appPrefs, Optional.of(new VersionedPreferences(oldVersion, olderPrefs))); - classUnderTest.saveTimestampWhenAskedForImport(); + classUnderTest.documentThatNoImportIsDesired(); assertFalse(classUnderTest.askForImportIfOlderSettingsExist()); - - } - + @Test public void that_run_after_import_action_is_executed_in_tryImportingPreviousVersionSettings() { Set responses = new HashSet<>(); Runnable action = () -> responses.add("action performed"); - + AppVersion oldVersion = new AppVersion(0, 9); Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); olderPrefs.put("somekey", "1234"); - + Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); Optional previousVersionSettings = Optional.of(new VersionedPreferences(oldVersion, olderPrefs)); - + classUnderTest = new PreferencesImporter(appPrefs, previousVersionSettings); classUnderTest.runAfterImport(action); classUnderTest.tryImportingPreviousVersionSettings(); assertTrue(responses.contains("action performed")); assertEquals("1234", appPrefs.get("somekey", null)); - assertNotNull(appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, null)); + assertNotNull(appPrefs.get(PreferencesImporter.PREF_PERFORM_IMPORT, null)); } @Test @@ -116,54 +115,60 @@ public void that_null_value_for_run_after_import_action_is_not_accepted() { assertThrows(NullPointerException.class, () -> classUnderTest.runAfterImport(null)); } - + @Test public void that_user_interaction_is_executed_and_user_opt_out_decision_is_documented() throws Exception { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET"); appPrefs.clear(); - + classUnderTest = new PreferencesImporter(appPrefs, Optional.empty()); - + Set interactionResponses = new HashSet<>(); - + Supplier> userInteraction = () -> { interactionResponses.add("alert-opened"); return Optional.empty(); }; - + classUnderTest.executeInteractionAndImport(userInteraction); - String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); - - assertTrue(interactionResponses.contains("alert-opened")); - assertTrue(documentedUserDecision.contains("-no-import")); + String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_PERFORM_IMPORT, ""); + assertEquals("false",documentedUserDecision); } @Test public void that_user_interaction_is_executed_and_user_opted_in() throws Exception { Preferences appPrefs = Preferences.userRoot().node("SB_TEST_TARGET2"); appPrefs.clear(); - + AppVersion oldVersion = new AppVersion(0, 9); Preferences olderPrefs = Preferences.userRoot().node("SB_OLD_VER"); olderPrefs.put("somekey", "1234"); - + Optional previousVersionSettings = Optional.of(new VersionedPreferences(oldVersion, olderPrefs)); - + classUnderTest = new PreferencesImporter(appPrefs, previousVersionSettings); - + Set interactionResponses = new HashSet<>(); - + Supplier> userInteraction = () -> { interactionResponses.add("alert-opened"); return Optional.of(ButtonType.YES); }; - + classUnderTest.askForActionAndRun(userInteraction); - String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_ASKED_FOR_IMPORT, ""); - + String documentedUserDecision = appPrefs.get(PreferencesImporter.PREF_PERFORM_IMPORT, ""); + assertTrue(interactionResponses.contains("alert-opened")); assertFalse(documentedUserDecision.contains("-no-import")); assertNotEquals("", documentedUserDecision); } + /** + * This is only for debugging so that the import decision can be removed if needed + */ + public static void main(String[] args) { + PreferencesController.getSingleton() + .getImporter() + .clearImportDecision(); + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java index 2a7c36a7c..eec87d9b4 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PrefsHelper.java @@ -31,6 +31,7 @@ */ package com.oracle.javafx.scenebuilder.app.preferences; +import java.lang.reflect.Field; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; @@ -40,10 +41,13 @@ * Utility Class to help preparing Preferences nodes for tests */ public class PrefsHelper { + private PrefsHelper() { /* not intended for instantiation */ } + public static void removeAllNonReleaseNodes(Preferences prefs) { + System.out.println(prefs); String appVersion = AppSettings.getSceneBuilderVersion(); String versionSpecificNode = PreferencesController.SB_RELEASE_NODE_PREFIX + appVersion; try { @@ -58,7 +62,7 @@ public static void removeAllNonReleaseNodes(Preferences prefs) { throw new RuntimeException(e); } } - + public static void removeAllChildNodes(Preferences prefs) { try { String[] childnodes = prefs.childrenNames(); @@ -70,4 +74,15 @@ public static void removeAllChildNodes(Preferences prefs) { throw new RuntimeException(e); } } + + public static synchronized void setToNull(Object source, String fieldName) { + try { + Class sourceClass = source.getClass(); + Field field = sourceClass.getDeclaredField(fieldName); + field.setAccessible(true); + field.set(source, null); + } catch (Exception error) { + throw new RuntimeException(error); + } + } } diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java index d10713b30..a4b86351f 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java @@ -50,8 +50,9 @@ public class VersionedPreferencesFinderTest { @BeforeClass public static void setup() { testNode = Preferences.userRoot() - .node("/SBTEST") - .node("/com/oracle/javafx/scenebuilder/app/preferences"); + .node("SBTEST") + .node("com/oracle/javafx/scenebuilder/app/preferences"); + PrefsHelper.removeAllChildNodes(testNode); classUnderTest = new VersionedPreferencesFinder("SB_", testNode); } diff --git a/docs/snippets/AppConfiguration.md b/docs/snippets/AppConfiguration.md index 7dd7beb62..279f6ffcb 100644 --- a/docs/snippets/AppConfiguration.md +++ b/docs/snippets/AppConfiguration.md @@ -5,7 +5,6 @@ | Location | Responsible Classes | Description | | :---------------------- | :--------------------------------------------------------- | :---------- | | applicationDataFolder | `c.o.j.scenebuilder.app.AppPlatform` | Message box and user library folders are located here | -| messageBoxFolder | `c.o.j.scenebuilder.app.AppPlatform` | Contents of message box | | userLibraryFolder | `c.o.j.scenebuilder.app.AppPlatform` | May contain JAR file with a JavaFX controls inside | | logsFolder | `c.o.j.scenebuilder.app.AppPlatform` | Here the `scenebuilder-x.y.z.log` file is stored, usually inside the users profiles directory | | Java Preferences | `c.o.j.scenebuilder.app.preferences.PreferencesController` | Standardized persistent storage of application settings | @@ -31,27 +30,35 @@ | ---------------------- | | `applicationDataFolder/MB` | -### LogsFolder +### Log File | Version | Location | | ------- | ---------------------------------- | -| <= 17 | `%USERPROFILE%\.scenebuilder\logs` | -| >= 18 | `%USERPROFILE%\.scenebuilder-18.0.0\logs` | +| <= 17 | `%USERPROFILE%\.scenebuilder\logs\scenebuilder.log` | +| >= 18 | `%USERPROFILE%\.scenebuilder\logs\scenebuilder-18.0.0.log` | ### Preferences -Node Structure until version 17: - -Root Node: `com.oracle.javafx.scenebuilder.app.preferences` - * SB_2.0 (`IMPORTED_GLUON_JARS`, `LAST_SENT_TRACKING_INFO_DATE`, `RECENT_ITEMS`, `REGISTRATION_EMAIL`, `REGISTRATION_HASH`, `REGISTRATION_OPT_IN`) - * ARTIFACTS - * DOCUMENTS (separate node for each document's settings) - * REPOSITIRIES - -Structure with versions >= 18.0.0: - -Root Node: `com.oracle.javafx.scenebuilder.app.preferences` - * SB_18.0.0 - * ARTIFACTS - * DOCUMENTS - * REPOSITIRIES +Scene Builder stores its preferences in following root node: + +| Node | Description | +|------|-------------| +| `com.oracle.javafx.scenebuilder.app.preferences` | root node | +| `com.oracle.javafx.scenebuilder.app.preferences.SB_2.0` | Preferences for all versions <= 17 | +| `com.oracle.javafx.scenebuilder.app.preferences.SB_18.0.0` | Preferences for all versions > 17 | + +Each node may hold following child nodes: + +| Child Node | Description | +|------------|-------------| +| `ARTIFACTS` | tbd. | +| `DOCUMENTS` | one child node per recent document | +| `REPOSITORIES` | tbd. | + +SB_2.0 preference keys: +* `IMPORTED_GLUON_JARS`, `LAST_SENT_TRACKING_INFO_DATE`, `RECENT_ITEMS`, `REGISTRATION_EMAIL`, `REGISTRATION_HASH`, `REGISTRATION_OPT_IN` + +SB_18.0.0 preference keys: +* same like 2.0 and: +* `PERFORM_IMPORT` = `true|false` , if missing or true, app preferences will be imported +* `IMPORT_USER_LIBRARY` = `true|false` , if missing or true, user library directory will be imported diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java index a4a97fae6..b36a9dbe3 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/preset/MavenPresets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -38,14 +38,12 @@ public class MavenPresets { public static final String MAVEN = "Maven Central"; - public static final String JCENTER = "Jcenter"; public static final String SONATYPE = "Sonatype"; public static final String GLUON_NEXUS = "Gluon Nexus"; public static final String LOCAL = "Local"; private static final List REPOSITORIES = Arrays.asList( new Repository(MAVEN, "default", "https://repo1.maven.org/maven2/"), - new Repository(JCENTER, "default", "https://jcenter.bintray.com"), new Repository(SONATYPE + " (snapshots)", "default", "https://oss.sonatype.org/content/repositories/snapshots"), new Repository(SONATYPE + " (releases)", "default", "https://oss.sonatype.org/content/repositories/releases"), new Repository(GLUON_NEXUS + " (releases)", "default", "https://nexus.gluonhq.com/nexus/content/repositories/releases")); diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java deleted file mode 100644 index 0822d653c..000000000 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/JcenterSearch.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2016, Gluon and/or its affiliates. - * All rights reserved. Use is subject to license terms. - * - * This file is available and licensed under the following license: - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * - Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the distribution. - * - Neither the name of Oracle Corporation and Gluon nor the names of its - * contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.search; - -import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.preset.MavenPresets; -import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonReader; -import static org.apache.commons.codec.binary.Base64.encodeBase64; -import org.apache.http.HttpResponse; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.impl.client.HttpClients; -import org.eclipse.aether.artifact.DefaultArtifact; - -public class JcenterSearch implements Search { - - // bintray - - // This requires authentication: -// private static final String URL_PREFIX = "https://api.bintray.com/search/packages?name="; - // This doesn't require authentication, limited to 50 results: - private static final String URL_PREFIX = "https://api.bintray.com/search/packages/maven?q=*"; - private static final String URL_SUFFIX = "*"; - - private final HttpClient client; - private final String username; - private final String password; - - public JcenterSearch(String username, String password) { - client = HttpClients.createDefault(); - this.username = username; - this.password = password; - } - - @Override - public List getCoordinates(String query) { - final Map map = new HashMap<>(); - map.put("Repository", MavenPresets.JCENTER); - - try { - HttpGet request = new HttpGet(URL_PREFIX + query + URL_SUFFIX); - if (!username.isEmpty() && !password.isEmpty()) { - String authStringEnc = new String(encodeBase64((username + ":" + password).getBytes())); - request.addHeader("Authorization", "Basic " + authStringEnc); - } - request.setHeader("Accept", "application/json"); - HttpResponse response = client.execute(request); - try (JsonReader rdr = Json.createReader(response.getEntity().getContent())) { - JsonArray obj = rdr.readArray(); - if (obj != null && !obj.isEmpty()) { - return obj.getValuesAs(JsonObject.class) - .stream() - .map(o -> { - JsonArray ids = o.getJsonArray("system_ids"); - if (ids != null && !ids.isEmpty()) { - return ids.stream() - .map(j -> j.toString().replaceAll("\"", "") + ":" + MIN_VERSION) - .collect(Collectors.toList()); - } - return null; - }) - .filter(Objects::nonNull) - .flatMap(l -> l.stream()) - .distinct() - .map(gav -> new DefaultArtifact(gav, map)) - .collect(Collectors.toList()); - } - } - } catch (IOException ex) { - Logger.getLogger(JcenterSearch.class.getName()).log(Level.SEVERE, null, ex); - } - return null; - } - -} diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java index 0437fd7bc..add23d0fc 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/editor/panel/library/maven/search/SearchService.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, Gluon and/or its affiliates. + * Copyright (c) 2016, 2022, Gluon and/or its affiliates. * All rights reserved. Use is subject to license terms. * * This file is available and licensed under the following license: @@ -111,7 +111,6 @@ protected Void call() throws Exception { tasks = Arrays.asList( createSearchTask(new MavenSearch()), createSearchTask(new NexusSearch(MavenPresets.SONATYPE, "http://oss.sonatype.org", "", "")), - createSearchTask(new JcenterSearch("", "")), createSearchTask(new NexusSearch(MavenPresets.GLUON_NEXUS, "https://nexus.gluonhq.com/nexus", "", "")), createSearchTask(new LocalSearch(userM2Repository))); diff --git a/pom.xml b/pom.xml index 00532d14a..433393059 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,7 @@ ${main.class.name} + From 89bcc3d8dbf1b56d3315d241de04125e4cafeea3 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 23 Jan 2022 23:13:54 +0100 Subject: [PATCH 33/42] Reduced method visibility. --- .../app/preferences/PreferencesImporter.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index cb3eb9046..ee62f605d 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -152,7 +152,7 @@ void clearImportDecision() { * Attempts to import settings of a previous version if existing. * There is no user feedback in case of error. The ope */ - public void tryImportingPreviousVersionSettings() { + void tryImportingPreviousVersionSettings() { if (this.optionalSourceNode.isPresent()) { VersionedPreferences source = this.optionalSourceNode.get(); try { @@ -175,7 +175,7 @@ public void tryImportingPreviousVersionSettings() { * * @return true when previous version settings have been found and user has not yet decided */ - public boolean askForImportIfOlderSettingsExist() { + boolean askForImportIfOlderSettingsExist() { boolean previousVersionFound = this.optionalSourceNode.isPresent(); logger.log(Level.FINE, "older preferences detected: {0}", previousVersionFound); if (previousVersionFound) { @@ -200,13 +200,16 @@ private boolean importForced() { * @param action {@link Runnable} * @throws NullPointerException when action is null */ - public void runAfterImport(Runnable action) { + void runAfterImport(Runnable action) { this.actionAfterImport = Objects.requireNonNull(action); } /** - * Will raise a JavaFX {@link Alert} to ask the user whether to import previous version settings or not. - * The question will only appear in cases where previous version settings exist and the user decision has not been saved yet. + * Will raise a JavaFX {@link Alert} to ask the user whether to import previous + * version settings or not. The question will only appear in cases where + * previous version settings exist and the user decision has not been saved yet. + * + * @return true when user decided to do the import. */ public boolean askForActionAndRun() { Supplier> alertInteraction = () -> { @@ -214,6 +217,7 @@ public boolean askForActionAndRun() { customAlert.setTitle("Gluon Scene Builder"); customAlert.setHeaderText("Import settings"); + customAlert.setHeight(350); customAlert.setContentText( "Previous version settings found. Do you want to import those?" + "\nScene Builder will also import the JAR files from your library." From 8e1593dc2675ba179e4a9b51a674251744145126 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 23 Jan 2022 23:24:13 +0100 Subject: [PATCH 34/42] Icon added to dialog. --- .../app/preferences/PreferencesImporter.java | 2 ++ .../javafx/scenebuilder/kit/alert/SBAlert.java | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index ee62f605d..d7a73f7eb 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -39,6 +39,7 @@ import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; +import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.alert.SBAlert; import javafx.scene.control.Alert; @@ -218,6 +219,7 @@ public boolean askForActionAndRun() { customAlert.setTitle("Gluon Scene Builder"); customAlert.setHeaderText("Import settings"); customAlert.setHeight(350); + customAlert.setIcons(AppSettings.APP_ICON_16, AppSettings.APP_ICON_32); customAlert.setContentText( "Previous version settings found. Do you want to import those?" + "\nScene Builder will also import the JAR files from your library." diff --git a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java index def6c49cc..c59e530db 100644 --- a/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java +++ b/kit/src/main/java/com/oracle/javafx/scenebuilder/kit/alert/SBAlert.java @@ -31,8 +31,13 @@ */ package com.oracle.javafx.scenebuilder.kit.alert; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; +import javafx.scene.image.Image; import javafx.stage.Stage; /** @@ -60,5 +65,14 @@ private void setIcons(Stage owner) { alertStage.getIcons().setAll(owner.getIcons()); } + public void setIcons(String... iconResources) { + List icons = Stream.of(iconResources) + .map(Image::new) + .collect(Collectors.toList()); + + Stage alertStage = (Stage) getDialogPane().getScene().getWindow(); + alertStage.getIcons().addAll(icons); + } + } From 425abd03706d0e1a0a2541515f79d85184f8fe36 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 23 Jan 2022 23:38:41 +0100 Subject: [PATCH 35/42] KIT and APP now use Java-17 again. Eclipse does not like different versions. --- app/pom.xml | 1 - pom.xml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index 7bb5f83c8..d87047088 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -17,7 +17,6 @@ ${java.version}, ${java.runtime.name} ${javafx.version} yyyy-MM-dd HH:mm:ss - 17 diff --git a/pom.xml b/pom.xml index 433393059..2658e5723 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ 1.1.0 6.1.0 4.0.13 - 11 + 17 UTF-8 From 765d60be8cf76f82ffa1838c60788f31951c4349 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Sun, 23 Jan 2022 23:41:28 +0100 Subject: [PATCH 36/42] Renamed method according to feedback. --- .../scenebuilder/app/library/user/UserLibraryImporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index 347f6461b..eb19acb59 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -91,7 +91,7 @@ AppPlatformDirectories getPlatformDirectories() { * @return {@link ImportResult} describing how the activity was completed. */ ImportResult performImportWhenDesired() { - boolean forcedImport = importForced(); + boolean forcedImport = isImportForced(); boolean requiresImport = preferences.getBoolean(PREF_IMPORT_USER_LIBRARY, true); if (requiresImport || forcedImport) { if (forcedImport) { @@ -103,7 +103,7 @@ ImportResult performImportWhenDesired() { return ImportResult.SKIPPED; } - private boolean importForced() { + private boolean isImportForced() { String forceImport = System.getProperty("forceImport", "false"); return "true".equalsIgnoreCase(forceImport); } From 7060a3836ba42e3363f05d4c77b88767b7334751 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Mon, 24 Jan 2022 00:01:52 +0100 Subject: [PATCH 37/42] State model for return codes corrected. --- .../app/library/user/UserLibraryImporter.java | 42 ++++++++++++------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index eb19acb59..f6da0d01d 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -36,11 +36,13 @@ import java.nio.file.Path; import java.nio.file.StandardCopyOption; import java.util.Collections; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.prefs.Preferences; @@ -129,39 +131,48 @@ ImportResult importPreviousVersionUserLibrary() { private ImportResult importUserLibraryContentsFrom(Path sourceSettings, Path actualLibraryDir) { Path oldLibrary = sourceSettings.resolve("Library"); - ImportResult status = ImportResult.COMPLETED; + Set results = EnumSet.noneOf(ImportResult.class); if (Files.exists(oldLibrary) && Files.isDirectory(oldLibrary)) { logger.log(Level.INFO, oldLibrary.toAbsolutePath().toString()); try { - copyDirectory(oldLibrary, actualLibraryDir); + copyDirectory(oldLibrary, actualLibraryDir, results); } catch (IOException e) { String template = "Failed to import user library from %s"; String message = String.format(template, oldLibrary); logger.log(Level.SEVERE, message, e); - status = ImportResult.COMPLETED_WITH_ERRORS; + results.add(ImportResult.FAILED); } } documentThatImportIsDone(); - return status; + + if (!results.contains(ImportResult.COMPLETED)) { + return ImportResult.FAILED; + } + if (results.contains(ImportResult.FAILED)) { + return ImportResult.COMPLETED_WITH_ERRORS; + } + return ImportResult.COMPLETED; } - void copyDirectory(Path sourceDir, Path destinationDir) throws IOException { + void copyDirectory(Path sourceDir, Path destinationDir,Set results) throws IOException { Files.walk(sourceDir).forEach(item -> - adjustDestinationPathAndTryCopy(sourceDir, destinationDir, item)); + adjustDestinationPathAndTryCopy(sourceDir, destinationDir, item,results)); } - void adjustDestinationPathAndTryCopy(Path sourceDir, Path destinationDir, Path source) { + void adjustDestinationPathAndTryCopy(Path sourceDir, Path destinationDir, Path source, Set results) { Path relativeSrc = sourceDir.relativize(source); Path destination = destinationDir.resolve(relativeSrc).toAbsolutePath(); - tryFileCopy(source, destination); + results.add(tryFileCopy(source, destination)); } - void tryFileCopy(Path source, Path destination) { + ImportResult tryFileCopy(Path source, Path destination) { try { createDirectoryIfNeeded(destination); copyFile(source, destination); + return ImportResult.COMPLETED; } catch (IOException e) { logger.log(Level.SEVERE, "Import failed", e); + return ImportResult.FAILED; } } @@ -272,12 +283,6 @@ enum ImportResult { */ COMPLETED, - - /** - * User library import failed with an error. - */ - FAILED, - /** * User library import was skipped as user opted out. */ @@ -289,7 +294,12 @@ enum ImportResult { NOTHING_TO_IMPORT, /** - * User library was imported but there were errors importing individual JARs. + * User library import failed. + */ + FAILED, + + /** + * User library import completed but there were errors importing individual JARs. */ COMPLETED_WITH_ERRORS; } From e3e2d84170749fd7ab9079fbb50e429b9f163df8 Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Mon, 24 Jan 2022 00:04:35 +0100 Subject: [PATCH 38/42] Cleanup --- .../app/library/user/UserLibraryImporter.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index f6da0d01d..f39a5074f 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -62,7 +62,7 @@ public final class UserLibraryImporter { protected static final String PREF_IMPORT_USER_LIBRARY = "IMPORT_USER_LIBRARY"; - + private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; private final Preferences preferences; @@ -81,7 +81,7 @@ public UserLibraryImporter(Optional appVersion, AppPlatformDirectori this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); } - + AppPlatformDirectories getPlatformDirectories() { return this.appDirectories; } @@ -144,7 +144,7 @@ private ImportResult importUserLibraryContentsFrom(Path sourceSettings, Path act } } documentThatImportIsDone(); - + if (!results.contains(ImportResult.COMPLETED)) { return ImportResult.FAILED; } @@ -191,7 +191,8 @@ void createDirectoryIfNeeded(Path destination) throws IOException { private List collectLibraryCandidates(Path appData) { try (Stream files = Files.list(appData)) { - return files.filter(Files::isDirectory).collect(Collectors.toList()); + return files.filter(Files::isDirectory) + .collect(Collectors.toList()); } catch (IOException e) { logger.log(Level.SEVERE, "Error while searching previous version user library locations.", e); @@ -233,12 +234,11 @@ Map collectPreviousVersionLibraryDirs(List candidates) { } } } - return sceneBuilderDirs; } static class UserLibraryImportTask extends Task { - + private final Logger logger = Logger.getLogger(UserLibraryImportTask.class.getName()); private final UserLibraryImporter userLibImporter; From 35065c6a6135e74187198710116b11b51acd50eb Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Mon, 24 Jan 2022 00:46:28 +0100 Subject: [PATCH 39/42] Logging improved. Removed the need for the boolean variable in app. --- .../scenebuilder/app/SceneBuilderApp.java | 6 +-- .../app/library/user/UserLibraryImporter.java | 34 ++++++++++++----- .../app/preferences/PreferencesImporter.java | 37 ++++++++++++------- 3 files changed, 51 insertions(+), 26 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java index d927eb422..503b85a0b 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/SceneBuilderApp.java @@ -372,7 +372,7 @@ public void handleLaunch(List files) { setApplicationUncaughtExceptionHandler(); PreferencesImporter prefsImporter = PreferencesController.getSingleton().getImporter(); - boolean doImport = prefsImporter.askForActionAndRun(); + prefsImporter.askForActionAndRun(); AppPlatform.getAppDirectories().createUserLibraryFolder(); MavenPreferences mavenPreferences = PreferencesController.getSingleton().getMavenPreferences(); @@ -416,9 +416,7 @@ public void handleLaunch(List files) { // The import will start as soon as the user library is initialized. // Import status, if triggered, if failed or completed, will be written to log. - if (doImport) { - Platform.runLater(UserLibraryImporter.createImportTask()); - } + Platform.runLater(UserLibraryImporter.createImportTask()); sendTrackingStartupInfo(); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index f39a5074f..e437e4b48 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -34,7 +34,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; @@ -61,6 +60,14 @@ public final class UserLibraryImporter { + public static void forceImport(Preferences target) { + target.remove(PREF_IMPORT_USER_LIBRARY); + } + + public static void disableImport(Preferences target) { + target.putBoolean(PREF_IMPORT_USER_LIBRARY, false); + } + protected static final String PREF_IMPORT_USER_LIBRARY = "IMPORT_USER_LIBRARY"; private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); @@ -88,8 +95,13 @@ AppPlatformDirectories getPlatformDirectories() { /** * The library import is only performed when the property key - * {@code IMPORT_USER_LIBRARY} does not exist or is defined as "true". - * + * {@code IMPORT_USER_LIBRARY} does not exist or is defined as "true". Only + * files which do not already exist in target library directory will be copied + * from source directory. + * + * There will be no error message, the process is fully silent. In case of + * errors the log file shall be consulted. + * * @return {@link ImportResult} describing how the activity was completed. */ ImportResult performImportWhenDesired() { @@ -178,8 +190,12 @@ ImportResult tryFileCopy(Path source, Path destination) { void copyFile(Path source, Path destination) throws IOException { if (!Files.isDirectory(source)) { - logger.log(Level.INFO, "Importing {0} into {1}", new Object[] { source, destination }); - Files.copy(source, destination, StandardCopyOption.REPLACE_EXISTING); + if (!Files.exists(destination)) { + logger.log(Level.INFO, "Importing {0} into {1}", new Object[] { source, destination }); + Files.copy(source, destination); + } else { + logger.log(Level.INFO, "skipping {0} as it already exists", new Object[] { destination.getFileName() }); + } } } @@ -244,8 +260,8 @@ static class UserLibraryImportTask extends Task { UserLibraryImportTask(UserLibraryImporter userLibImporter) { this.userLibImporter = userLibImporter; - setOnFailed(this::logException); - setOnSucceeded(this::logSuccess); + setOnFailed(this::completedWithError); + setOnSucceeded(this::completedSuccessful); } @Override @@ -253,7 +269,7 @@ protected ImportResult call() throws Exception { return userLibImporter.performImportWhenDesired(); } - private void logException(WorkerStateEvent event) { + private void completedWithError(WorkerStateEvent event) { if (getException() != null) { logger.log(Level.SEVERE, "Import of User Library failed with error!", getException()); @@ -263,7 +279,7 @@ private void logException(WorkerStateEvent event) { } } - private void logSuccess(WorkerStateEvent event) { + private void completedSuccessful(WorkerStateEvent event) { logger.log(Level.SEVERE, "User Library Import finished with: {0}", getValue()); } diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index d7a73f7eb..e9b7260f0 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -39,6 +39,7 @@ import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; +import com.oracle.javafx.scenebuilder.app.library.user.UserLibraryImporter; import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.alert.SBAlert; @@ -178,17 +179,27 @@ void tryImportingPreviousVersionSettings() { */ boolean askForImportIfOlderSettingsExist() { boolean previousVersionFound = this.optionalSourceNode.isPresent(); - logger.log(Level.FINE, "older preferences detected: {0}", previousVersionFound); - if (previousVersionFound) { - VersionedPreferences prefs = this.optionalSourceNode.get(); - logger.log(Level.FINE, "Version: {0}", prefs.version()); - logger.log(Level.FINE, "Node : {0}", prefs.node()); + if (!previousVersionFound) { + return false; } + + logger.log(Level.FINE, "Importing previous version preferences"); + VersionedPreferences prefs = this.optionalSourceNode.get(); + logger.log(Level.FINE, "Version: {0}", prefs.version()); + logger.log(Level.FINE, "Node : {0}", prefs.node()); + boolean forcedImport = importForced(); if (forcedImport) { logger.log(Level.FINE, "detected -DforceImport=true"); + return true; + } + + boolean importRequired = askForImport(); + if (!importRequired) { + logger.log(Level.FINE, "Import was already performed, disabling user library import."); + UserLibraryImporter.disableImport(target); } - return previousVersionFound && (forcedImport || askForImport()); + return importRequired; } private boolean importForced() { @@ -209,10 +220,11 @@ void runAfterImport(Runnable action) { * Will raise a JavaFX {@link Alert} to ask the user whether to import previous * version settings or not. The question will only appear in cases where * previous version settings exist and the user decision has not been saved yet. - * - * @return true when user decided to do the import. + *

+ * If the user declines to perform the import, it will also prevent the user + * library import by setting the corresponding preference value. */ - public boolean askForActionAndRun() { + public void askForActionAndRun() { Supplier> alertInteraction = () -> { SBAlert customAlert = new SBAlert(AlertType.CONFIRMATION, ButtonType.YES, ButtonType.NO); @@ -226,14 +238,13 @@ public boolean askForActionAndRun() { + "\n\nYour decision will be remembered so you won't be asked again."); return customAlert.showAndWait(); }; - return askForActionAndRun(alertInteraction); + askForActionAndRun(alertInteraction); } - boolean askForActionAndRun(Supplier> alertInteraction) { + void askForActionAndRun(Supplier> alertInteraction) { if (askForImportIfOlderSettingsExist()) { - return executeInteractionAndImport(alertInteraction); + executeInteractionAndImport(alertInteraction); } - return false; } boolean executeInteractionAndImport(Supplier> alertInteraction) { From 936cd7fddaa25ffd3d2b6da12c5d303667468941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Mon, 24 Jan 2022 09:00:43 +0100 Subject: [PATCH 40/42] Naming update --- .../scenebuilder/app/preferences/PreferencesImporter.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index e9b7260f0..706ed0bd2 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -188,7 +188,7 @@ boolean askForImportIfOlderSettingsExist() { logger.log(Level.FINE, "Version: {0}", prefs.version()); logger.log(Level.FINE, "Node : {0}", prefs.node()); - boolean forcedImport = importForced(); + boolean forcedImport = isForcedImport(); if (forcedImport) { logger.log(Level.FINE, "detected -DforceImport=true"); return true; @@ -202,7 +202,7 @@ boolean askForImportIfOlderSettingsExist() { return importRequired; } - private boolean importForced() { + private boolean isForcedImport() { String forceImport = System.getProperty("forceImport", "false"); return "true".equalsIgnoreCase(forceImport); } From dc11ea8d1e6a575c64b89c01f8d3e0272e66f27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20L=C3=B6ffler?= Date: Mon, 24 Jan 2022 09:04:18 +0100 Subject: [PATCH 41/42] Further reduced visibility of class elements. --- .../app/library/user/UserLibraryImporter.java | 18 +++++++++++++++--- .../app/preferences/PreferencesImporter.java | 2 +- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java index e437e4b48..c184a5d91 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/library/user/UserLibraryImporter.java @@ -68,7 +68,19 @@ public static void disableImport(Preferences target) { target.putBoolean(PREF_IMPORT_USER_LIBRARY, false); } - protected static final String PREF_IMPORT_USER_LIBRARY = "IMPORT_USER_LIBRARY"; + /*************************************************************************** + * * + * Static fields * + * * + **************************************************************************/ + + static final String PREF_IMPORT_USER_LIBRARY = "IMPORT_USER_LIBRARY"; + + /*************************************************************************** + * * + * Instance fields * + * * + **************************************************************************/ private final Logger logger = Logger.getLogger(PreferencesImporter.class.getName()); private final Optional version; @@ -79,11 +91,11 @@ public UserLibraryImporter(Preferences applicationPreferences) { this(AppPlatform.getAppDirectories(), applicationPreferences); } - public UserLibraryImporter(AppPlatformDirectories directories, Preferences applicationPreferences) { + UserLibraryImporter(AppPlatformDirectories directories, Preferences applicationPreferences) { this(AppVersion.fromString(AppSettings.getSceneBuilderVersion()), directories, applicationPreferences); } - public UserLibraryImporter(Optional appVersion, AppPlatformDirectories appDirectories, Preferences applicationPreferences) { + UserLibraryImporter(Optional appVersion, AppPlatformDirectories appDirectories, Preferences applicationPreferences) { this.version = Objects.requireNonNull(appVersion); this.preferences = Objects.requireNonNull(applicationPreferences); this.appDirectories = Objects.requireNonNull(appDirectories); diff --git a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java index 706ed0bd2..bd8c1d102 100644 --- a/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java +++ b/app/src/main/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporter.java @@ -59,7 +59,6 @@ public final class PreferencesImporter { * * **************************************************************************/ - // GLOBAL PREFERENCES public static final String PREF_PERFORM_IMPORT = "PERFORM_IMPORT"; /*************************************************************************** @@ -72,6 +71,7 @@ public final class PreferencesImporter { private final Preferences target; private final Optional optionalSourceNode; private Runnable actionAfterImport; + /** * Creates a new Preferences importer. * From 5844f3f3cdd6bbde02c03630c4b6aeef0fa440fd Mon Sep 17 00:00:00 2001 From: Oliver-Loeffler Date: Thu, 10 Feb 2022 19:04:58 +0100 Subject: [PATCH 42/42] Replaced JUnit4 with JUnit5 --- app/pom.xml | 7 ------- .../javafx/scenebuilder/app/AppPlatformTest.java | 2 +- .../javafx/scenebuilder/app/OperatingSystemTest.java | 2 +- .../scenebuilder/app/preferences/AppVersionTest.java | 2 +- .../app/preferences/PreferencesControllerTest.java | 10 +++++----- .../app/preferences/PreferencesImporterTest.java | 10 +++++----- .../preferences/VersionedPreferencesFinderTest.java | 6 +++--- 7 files changed, 16 insertions(+), 23 deletions(-) diff --git a/app/pom.xml b/app/pom.xml index d520adf26..3a1cb52c1 100644 --- a/app/pom.xml +++ b/app/pom.xml @@ -25,13 +25,6 @@ kit ${project.version} - - - junit - junit - 4.13.2 - test - diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java index 547717fbe..5aaac6dda 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/AppPlatformTest.java @@ -39,7 +39,7 @@ import java.nio.file.Paths; import java.util.Locale; -import org.junit.Test; +import org.junit.jupiter.api.Test; import com.oracle.javafx.scenebuilder.app.util.AppSettings; diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java index 7e47e9363..af394f8dc 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/OperatingSystemTest.java @@ -34,7 +34,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assume.assumeTrue; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class OperatingSystemTest { diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java index 17657ffa5..c94534018 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/AppVersionTest.java @@ -36,7 +36,7 @@ import java.util.List; import java.util.Optional; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class AppVersionTest { diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java index 47cc90fea..7a356cef4 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesControllerTest.java @@ -40,9 +40,9 @@ import java.util.Optional; import java.util.prefs.Preferences; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import com.oracle.javafx.scenebuilder.app.util.AppSettings; import com.oracle.javafx.scenebuilder.kit.editor.panel.library.maven.repository.Repository; @@ -52,7 +52,7 @@ public class PreferencesControllerTest { private static Preferences testNode; private static PreferencesController classUnderTest; - @BeforeClass + @BeforeAll public static void setup() { testNode = Preferences.userRoot() .node("PREFS_CONTROLLER_TEST") @@ -63,7 +63,7 @@ public static void setup() { classUnderTest = PreferencesController.getSingleton(testNode); } - @AfterClass + @AfterAll public static void cleanup() throws Exception { Preferences.userRoot().node("PREFS_CONTROLLER_TEST").removeNode(); PrefsHelper.setToNull(PreferencesController.getSingleton(),"singleton"); diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java index 8cb254eb9..30e2a0084 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/PreferencesImporterTest.java @@ -13,9 +13,9 @@ import java.util.function.Supplier; import java.util.prefs.Preferences; -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import javafx.scene.control.ButtonType; @@ -23,8 +23,8 @@ public class PreferencesImporterTest { private PreferencesImporter classUnderTest; - @BeforeClass - @AfterClass + @BeforeAll + @AfterAll public static void cleanUpPrefs() throws Exception { Set nodesToBeRemoved = Set.of("SOURCE_TO_IMPORT", "SB_TEST_TARGET", diff --git a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java index a4b86351f..f09368e78 100644 --- a/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java +++ b/app/src/test/java/com/oracle/javafx/scenebuilder/app/preferences/VersionedPreferencesFinderTest.java @@ -38,8 +38,8 @@ import java.util.Optional; import java.util.prefs.Preferences; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; public class VersionedPreferencesFinderTest { @@ -47,7 +47,7 @@ public class VersionedPreferencesFinderTest { private static VersionedPreferencesFinder classUnderTest; - @BeforeClass + @BeforeAll public static void setup() { testNode = Preferences.userRoot() .node("SBTEST")