Skip to content

Commit

Permalink
Introduce 'user' preference scope located in <user.home>/.eclipse
Browse files Browse the repository at this point in the history
  • Loading branch information
HannesWell committed Jan 9, 2024
1 parent 5ba8030 commit b067575
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*******************************************************************************
* Copyright (c) 2024,2024 Hannes Wellmann and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Hannes Wellmann - Initial API and implementation
*******************************************************************************/
package org.eclipse.equinox.preferences.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;

import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.ConfigurationScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.core.runtime.preferences.UserScope;
import org.junit.Test;
import org.osgi.service.prefs.BackingStoreException;

public class ScopeStorageLocationTest {

@Test
public void testInstanceScope_writeLocation() throws Exception {
Path instanceLocation = getInstancePreferenceLocation();
assertWriteLocation(InstanceScope.INSTANCE, instanceLocation);
}

@Test
public void testInstanceScope_readLocation() throws Exception {
Path instanceLocation = getInstancePreferenceLocation();
assertReadLocation(InstanceScope.INSTANCE, instanceLocation);
}

private static Path getInstancePreferenceLocation() throws URISyntaxException {
return Path.of(Platform.getInstanceLocation().getURL().toURI())
.resolve(".metadata/.plugins/org.eclipse.core.runtime/");
}

@Test
public void testConfigurationScope_writeLocation() throws Exception {
Path configurationLocation = getConfigurationPreferenceLocation();
assertWriteLocation(ConfigurationScope.INSTANCE, configurationLocation);
}

@Test
public void testConfigurationScope_readLocation() throws Exception {
Path configurationLocation = getConfigurationPreferenceLocation();
assertReadLocation(ConfigurationScope.INSTANCE, configurationLocation);
}

private static Path getConfigurationPreferenceLocation() throws URISyntaxException {
return Path.of(Platform.getConfigurationLocation().getURL().toURI());
}

@Test
public void testUserScope_writeLocation() throws Exception {
Path configurationLocation = getUserPreferenenceLocation();
assertWriteLocation(UserScope.INSTANCE, configurationLocation);
}

@Test
public void testUserScope_readLocation() throws Exception {
Path configurationLocation = getUserPreferenenceLocation();
assertReadLocation(UserScope.INSTANCE, configurationLocation);
}

private static Path getUserPreferenenceLocation() {
return Path.of(System.getProperty("user.home") + "/.eclipse");
}

private static void assertWriteLocation(IScopeContext scope, Path expectedPreferenceLocation)
throws BackingStoreException, IOException {
Path expectedFileLocation = expectedPreferenceLocation.resolve(".settings/foo.bar.prefs");
assertFalse(Files.exists(expectedFileLocation));
try {
IEclipsePreferences node = scope.getNode("foo.bar");
node.putInt("someCount", 5);
node.flush();
String preferenceFileContent = Files.readString(expectedFileLocation);
assertEquals("""
eclipse.preferences.version=1
someCount=5
""".replace("\n", System.lineSeparator()), preferenceFileContent);
} finally {
Files.deleteIfExists(expectedFileLocation);
}
}

private static void assertReadLocation(IScopeContext scope, Path expectedPreferenceLocation)
throws BackingStoreException, IOException {
Path expectedFileLocation = expectedPreferenceLocation.resolve(".settings/foo.bar.buzz.prefs");
assertFalse(Files.exists(expectedFileLocation));
try {
Files.createDirectories(expectedFileLocation.getParent());
Files.writeString(expectedFileLocation, """
eclipse.preferences.version=1
aSetting=HelloWorld
""");
assertEquals("HelloWorld", scope.getNode("foo.bar.buzz").get("aSetting", null));
} finally {
Files.deleteIfExists(expectedFileLocation);
}

}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2004, 2023 IBM Corporation and others.
* Copyright (c) 2004, 2024 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -39,6 +39,7 @@ public class PreferencesService implements IPreferencesService {
private static List<String> DEFAULT_DEFAULT_LOOKUP_ORDER = List.of( //
InstanceScope.SCOPE, //
ConfigurationScope.SCOPE, //
UserScope.SCOPE, //
DefaultScope.SCOPE);
private static final char EXPORT_ROOT_PREFIX = '!';
private static final char BUNDLE_VERSION_PREFIX = '@';
Expand Down Expand Up @@ -67,6 +68,7 @@ private PreferencesService() {
initializeDefaultScope(DefaultScope.SCOPE, new DefaultPreferences());
initializeDefaultScope(InstanceScope.SCOPE, new InstancePreferences());
initializeDefaultScope(ConfigurationScope.SCOPE, new ConfigurationPreferences());
initializeDefaultScope(UserScope.SCOPE, new UserPreferences());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*******************************************************************************
* Copyright (c) 2023, 2024 Hannes Wellmann and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Hannes Wellmann - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.preferences;

import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.preferences.UserScope;

public class UserPreferences extends SingletonEclipsePreferences {

// cache which nodes have been loaded from disk
private static final Set<String> LOADED_NODES = ConcurrentHashMap.newKeySet();
private static final AtomicBoolean INITIALIZED = new AtomicBoolean();

/**
* Default constructor. Should only be called by #createExecutableExtension.
*/
public UserPreferences() {
this(null, null);
}

private UserPreferences(EclipsePreferences parent, String name) {
super(parent, name, LOADED_NODES, INITIALIZED);
}

@Override
IPath getBaseLocation() {
return UserScope.INSTANCE.getLocation();
}

@Override
protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
return new UserPreferences(nodeParent, nodeName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (c) 2023, 2024 Hannes Wellmann and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Hannes Wellmann - initial API and implementation
*******************************************************************************/
package org.eclipse.core.runtime.preferences;

import org.eclipse.core.internal.preferences.AbstractScope;
import org.eclipse.core.runtime.IPath;

/**
* @since 3.11
*/
public class UserScope extends AbstractScope {

/**
* String constant (value of <code>"user"</code>) used for the scope name for
* the user preference scope.
*/
public static final String SCOPE = "user"; //$NON-NLS-1$

private static final IPath USER_HOME_PREFERENCE_LOCATION;
static {
String userHome = System.getProperty("user.home"); //$NON-NLS-1$
USER_HOME_PREFERENCE_LOCATION = IPath.forWindows(userHome).append(".eclipse"); //$NON-NLS-1$
}

/**
* Singleton instance of a User Scope object. Typical usage is:
* <code>UserScope.INSTANCE.getNode(...);</code>
*
* @since 3.4
*/
public static final IScopeContext INSTANCE = new UserScope();

private UserScope() { // static use only via INSTANCE
}

@Override
public String getName() {
return SCOPE;
}

@Override
public IPath getLocation() {
return USER_HOME_PREFERENCE_LOCATION;
}

}

0 comments on commit b067575

Please sign in to comment.