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}