Introduce ISettings
diff --git a/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/Example.java b/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/Example.java
index 4a60b74..7fea85b 100644
--- a/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/Example.java
+++ b/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/Example.java
@@ -12,8 +12,8 @@
import org.eclipse.userstorage.IBlob;
import org.eclipse.userstorage.IStorage;
+import org.eclipse.userstorage.StorageFactory;
import org.eclipse.userstorage.internal.util.IOUtil;
-import org.eclipse.userstorage.spi.StorageFactory;
import org.eclipse.userstorage.util.FileStorageCache;
import java.io.FileOutputStream;
diff --git a/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/StorageTests.java b/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/StorageTests.java
index 1bf0e94..8eb7a69 100644
--- a/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/StorageTests.java
+++ b/org.eclipse.userstorage.tests/src/org/eclipse/userstorage/tests/StorageTests.java
@@ -15,11 +15,12 @@
import org.eclipse.userstorage.IBlob;
import org.eclipse.userstorage.IStorage;
import org.eclipse.userstorage.IStorageService;
+import org.eclipse.userstorage.StorageFactory;
import org.eclipse.userstorage.internal.Activator;
import org.eclipse.userstorage.internal.Session;
import org.eclipse.userstorage.internal.util.IOUtil;
import org.eclipse.userstorage.internal.util.StringUtil;
-import org.eclipse.userstorage.spi.StorageFactory;
+import org.eclipse.userstorage.spi.ISettings;
import org.eclipse.userstorage.tests.util.FixedCredentialsProvider;
import org.eclipse.userstorage.tests.util.USSServer;
import org.eclipse.userstorage.tests.util.USSServer.NOOPLogger;
@@ -96,14 +97,19 @@
final String serviceURI = "http://localhost:" + port;
service = IStorageService.Registry.INSTANCE.addService("Test Service", StringUtil.newURI(serviceURI));
- factory = new StorageFactory()
+ factory = new StorageFactory(new ISettings()
{
@Override
- protected String getPreferredServiceURI(String applicationToken)
+ public String getValue(String key) throws Exception
{
return serviceURI;
}
- };
+
+ @Override
+ public void setValue(String key, String value) throws Exception
+ {
+ }
+ });
}
IOUtil.deleteFiles(CACHE);
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
index 4b15f92..d2dc1b1 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/IBlob.java
@@ -11,7 +11,6 @@
package org.eclipse.userstorage;
import org.eclipse.userstorage.spi.StorageCache;
-import org.eclipse.userstorage.spi.StorageFactory;
import org.eclipse.userstorage.util.ConflictException;
import org.eclipse.userstorage.util.ProtocolException;
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/IStorage.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/IStorage.java
index 6a3d480..c375ed8 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/IStorage.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/IStorage.java
@@ -11,7 +11,6 @@
package org.eclipse.userstorage;
import org.eclipse.userstorage.spi.StorageCache;
-import org.eclipse.userstorage.spi.StorageFactory;
import org.eclipse.userstorage.util.BadKeyException;
/**
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/StorageFactory.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/StorageFactory.java
new file mode 100644
index 0000000..3f0be4d
--- /dev/null
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/StorageFactory.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.userstorage;
+
+import org.eclipse.userstorage.IStorageService.Registry;
+import org.eclipse.userstorage.internal.Activator;
+import org.eclipse.userstorage.internal.Storage;
+import org.eclipse.userstorage.internal.util.StringUtil;
+import org.eclipse.userstorage.spi.ISettings;
+import org.eclipse.userstorage.spi.StorageCache;
+import org.eclipse.userstorage.util.BadApplicationTokenException;
+import org.eclipse.userstorage.util.Settings;
+
+import java.util.NoSuchElementException;
+
+/**
+ * Creates {@link IStorage storages}.
+ *
+ * @author Eike Stepper
+ */
+public final class StorageFactory
+{
+ public static final StorageFactory DEFAULT = new StorageFactory(Settings.DEFAULT);
+
+ private final ISettings settings;
+
+ public StorageFactory(ISettings settings)
+ {
+ this.settings = settings;
+ }
+
+ /**
+ * Constructs this storage factory.
+ */
+ public StorageFactory()
+ {
+ this(new Settings.MemorySettings());
+ }
+
+ /**
+ * Returns the settings of this factory.
+ *
+ * @return the settings of this factory, never <code>null</code>.
+ */
+ public ISettings getSettings()
+ {
+ return settings;
+ }
+
+ /**
+ * Creates a storage for the application identified by the given application token.
+ * <p>
+ * Calling this method is identical to calling <code>create(applicationToken, null)</code>.
+ * <p>
+ *
+ * @param applicationToken the application token that identifies the application of the storage to be created.
+ * Minimal {@link BadApplicationTokenException#validate(String) lexical validation} is performed on the passed application token.<p>
+ * @return the newly created storage, never <code>null</code>.<p>
+ * @throws NoSuchElementException if the {@link Registry storage registry} is empty and, hence, there is no default storage available.<p>
+ * @throws BadApplicationTokenException if {@link BadApplicationTokenException#validate(String) lexical validation} of the passed application token fails.<p>
+ *
+ * @see #create(String, StorageCache)
+ */
+ public IStorage create(String applicationToken) throws NoSuchElementException, BadApplicationTokenException
+ {
+ return create(applicationToken, null);
+ }
+
+ /**
+ * Creates a storage for the application identified by the given application token and associates it with a given {@link StorageCache storage cache}.
+ * <p>
+ * @param applicationToken the application token that identifies the application of the storage to be created.
+ * Minimal {@link BadApplicationTokenException#validate(String) lexical validation} is performed on the passed application token.<p>
+ * @param cache a local storage cache to be used as a locally persistent optimization, or <code>null</code> if local caching is not wanted.<p>
+ * @return the newly created storage, never <code>null</code>.<p>
+ * @throws NoSuchElementException if the {@link Registry storage registry} is empty and, hence, there is no default storage available.<p>
+ * @throws BadApplicationTokenException if {@link BadApplicationTokenException#validate(String) lexical validation} of the passed application token fails.<p>
+ *
+ * @see #create(String)
+ * @see StorageCache
+ */
+ public IStorage create(String applicationToken, StorageCache cache) throws NoSuchElementException, BadApplicationTokenException
+ {
+ IStorageService service = getService(applicationToken);
+
+ Storage storage = new Storage(this, applicationToken, cache);
+ storage.setService(service);
+ return storage;
+ }
+
+ private IStorageService getService(String applicationToken) throws NoSuchElementException
+ {
+ if (!StringUtil.isEmpty(applicationToken))
+ {
+ IStorageService service = getPreferredService(applicationToken);
+ if (service != null)
+ {
+ return service;
+ }
+ }
+
+ IStorageService service = getPreferredService(null);
+ if (service != null)
+ {
+ return service;
+ }
+
+ IStorageService[] storages = IStorageService.Registry.INSTANCE.getServices();
+ if (storages.length != 0)
+ {
+ return storages[0];
+ }
+
+ throw new NoSuchElementException("No service registered");
+ }
+
+ private IStorageService getPreferredService(String applicationToken) throws NoSuchElementException
+ {
+ if (StringUtil.isEmpty(applicationToken))
+ {
+ applicationToken = "<default>";
+ }
+
+ try
+ {
+ String serviceURI = getPreferredServiceURI(applicationToken);
+ if (serviceURI != null)
+ {
+ return IStorageService.Registry.INSTANCE.getService(StringUtil.newURI(serviceURI));
+ }
+ }
+ catch (Exception ex)
+ {
+ //$FALL-THROUGH$
+ }
+
+ return null;
+ }
+
+ private String getPreferredServiceURI(String applicationToken)
+ {
+ try
+ {
+ return settings.getValue(applicationToken);
+ }
+ catch (Exception ex)
+ {
+ Activator.log(ex);
+ }
+
+ return null;
+ }
+}
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/DefaultStorageFactory.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/DefaultStorageFactory.java
deleted file mode 100644
index 9de4955..0000000
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/DefaultStorageFactory.java
+++ /dev/null
@@ -1,176 +0,0 @@
-/*
- * Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Eike Stepper - initial API and implementation
- */
-package org.eclipse.userstorage.internal;
-
-import org.eclipse.userstorage.spi.ISettings;
-import org.eclipse.userstorage.spi.StorageFactory;
-
-import org.eclipse.core.runtime.Platform;
-import org.eclipse.core.runtime.preferences.IEclipsePreferences;
-
-import org.osgi.service.prefs.BackingStoreException;
-import org.osgi.service.prefs.Preferences;
-
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * @author Eike Stepper
- */
-public final class DefaultStorageFactory extends StorageFactory
-{
- private final ISettings settings = createSettings();
-
- public DefaultStorageFactory()
- {
- }
-
- @Override
- protected String getPreferredServiceURI(String applicationToken)
- {
- try
- {
- return settings.getValue(applicationToken);
- }
- catch (Exception ex)
- {
- Activator.log(ex);
- }
-
- return null;
- }
-
- @Override
- protected void setPreferredServiceURI(String applicationToken, String serviceURI)
- {
- try
- {
- settings.setValue(applicationToken, serviceURI);
- }
- catch (Exception ex)
- {
- Activator.log(ex);
- }
- }
-
- private static ISettings createSettings()
- {
- String property = System.getProperty(StorageProperties.SETTINGS, null);
- if (property != null)
- {
- try
- {
- @SuppressWarnings("unchecked")
- Class<ISettings> c = (Class<ISettings>)Class.forName(property);
-
- return c.newInstance();
- }
- catch (Throwable ex)
- {
- Activator.log(ex);
- }
- }
-
- if (Activator.PLATFORM_RUNNING)
- {
- try
- {
- return new EclipseSettings("instance");
- }
- catch (BackingStoreException ex)
- {
- //$FALL-THROUGH$
- }
-
- try
- {
- return new EclipseSettings("configuration");
- }
- catch (BackingStoreException ex)
- {
- //$FALL-THROUGH$
- }
- }
-
- return new StandaloneSettings();
- }
-
- /**
- * @author Eike Stepper
- */
- private static final class EclipseSettings implements ISettings
- {
- private final Preferences node;
-
- public EclipseSettings(String scope) throws BackingStoreException
- {
- IEclipsePreferences rootNode = Platform.getPreferencesService().getRootNode();
- if (!rootNode.nodeExists(scope))
- {
- throw new BackingStoreException("Invalid scope: " + scope);
- }
-
- node = rootNode.node(scope).node(Activator.PLUGIN_ID);
- }
-
- @Override
- public String getValue(String key) throws Exception
- {
- return node.get(key, null);
- }
-
- @Override
- public void setValue(String key, String value) throws Exception
- {
- if (value == null)
- {
- node.remove(key);
- }
- else
- {
- node.put(key, value);
- }
-
- node.flush();
- }
- }
-
- /**
- * @author Eike Stepper
- */
- private static final class StandaloneSettings implements ISettings
- {
- private final Map<String, String> map = new HashMap<String, String>();
-
- public StandaloneSettings()
- {
- }
-
- @Override
- public String getValue(String key) throws Exception
- {
- return map.get(key);
- }
-
- @Override
- public void setValue(String key, String value) throws Exception
- {
- if (value == null)
- {
- map.remove(key);
- }
- else
- {
- map.put(key, value);
- }
- }
- }
-}
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/InternalStorageFactory.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/InternalStorageFactory.java
deleted file mode 100644
index f4d7270..0000000
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/InternalStorageFactory.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Eike Stepper - initial API and implementation
- */
-package org.eclipse.userstorage.internal;
-
-import org.eclipse.userstorage.IStorage;
-import org.eclipse.userstorage.IStorageService;
-import org.eclipse.userstorage.IStorageService.Registry;
-import org.eclipse.userstorage.internal.util.StringUtil;
-import org.eclipse.userstorage.spi.StorageCache;
-import org.eclipse.userstorage.util.BadApplicationTokenException;
-
-import java.util.NoSuchElementException;
-
-/**
- * @author Eike Stepper
- */
-public abstract class InternalStorageFactory
-{
- private static final Registry REGISTRY = IStorageService.Registry.INSTANCE;
-
- public InternalStorageFactory()
- {
- }
-
- public IStorage create(String applicationToken, StorageCache cache) throws NoSuchElementException, BadApplicationTokenException
- {
- IStorageService service = getService(applicationToken);
-
- Storage storage = new Storage(this, applicationToken, cache);
- storage.setService(service);
- return storage;
- }
-
- private IStorageService getService(String applicationToken) throws NoSuchElementException
- {
- try
- {
- String serviceURI = getPreferredServiceURI(applicationToken);
- if (serviceURI != null)
- {
- IStorageService service = REGISTRY.getService(StringUtil.newURI(serviceURI));
- if (service != null)
- {
- return service;
- }
- }
- }
- catch (Exception ex)
- {
- //$FALL-THROUGH$
- }
-
- try
- {
- String serviceURI = getPreferredServiceURI(null);
- if (serviceURI != null)
- {
- IStorageService service = REGISTRY.getService(StringUtil.newURI(serviceURI));
- if (service != null)
- {
- return service;
- }
- }
- }
- catch (Exception ex)
- {
- //$FALL-THROUGH$
- }
-
- IStorageService[] storages = REGISTRY.getServices();
- if (storages.length != 0)
- {
- return storages[0];
- }
-
- throw new NoSuchElementException("No service registered");
- }
-
- protected abstract String getPreferredServiceURI(String applicationToken);
-
- protected abstract void setPreferredServiceURI(String applicationToken, String serviceURI);
-}
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/Storage.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/Storage.java
index 66c40c2..97e8800 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/Storage.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/internal/Storage.java
@@ -13,8 +13,10 @@
import org.eclipse.userstorage.IBlob;
import org.eclipse.userstorage.IStorage;
import org.eclipse.userstorage.IStorageService;
+import org.eclipse.userstorage.StorageFactory;
import org.eclipse.userstorage.internal.util.IOUtil.TeeInputStream;
import org.eclipse.userstorage.internal.util.StringUtil;
+import org.eclipse.userstorage.spi.ISettings;
import org.eclipse.userstorage.spi.StorageCache;
import org.eclipse.userstorage.util.BadApplicationTokenException;
import org.eclipse.userstorage.util.ConflictException;
@@ -34,7 +36,7 @@
{
private final String applicationToken;
- private final InternalStorageFactory factory;
+ private final StorageFactory factory;
private final InternalStorageCache cache;
@@ -42,7 +44,7 @@
private StorageService service;
- public Storage(InternalStorageFactory factory, String applicationToken, InternalStorageCache cache) throws BadApplicationTokenException
+ public Storage(StorageFactory factory, String applicationToken, InternalStorageCache cache) throws BadApplicationTokenException
{
this.applicationToken = BadApplicationTokenException.validate(applicationToken);
this.factory = factory;
@@ -75,11 +77,10 @@
disposeBlobs();
this.service = (StorageService)service;
- factory.setPreferredServiceURI(applicationToken, service.getServiceURI().toString());
+ setPreferredServiceURI(service.getServiceURI().toString());
if (cache != null)
{
- // TODO Should this happen later when the new storage is accessed the first time?
cache.setService(service);
}
}
@@ -197,6 +198,19 @@
return service + " (" + applicationToken + ")";
}
+ private void setPreferredServiceURI(String serviceURI)
+ {
+ try
+ {
+ ISettings settings = factory.getSettings();
+ settings.setValue(applicationToken, serviceURI);
+ }
+ catch (Exception ex)
+ {
+ Activator.log(ex);
+ }
+ }
+
private void disposeBlobs()
{
for (Blob blob : blobs.values())
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ISettings.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ISettings.java
index 05075f9..8bad42f 100644
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ISettings.java
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/ISettings.java
@@ -12,6 +12,7 @@
import org.eclipse.userstorage.IStorage;
import org.eclipse.userstorage.IStorageService;
+import org.eclipse.userstorage.StorageFactory;
/**
* A generic key/value map used by the {@link StorageFactory#DEFAULT default storage factory}
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageFactory.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageFactory.java
deleted file mode 100644
index a6400c0..0000000
--- a/org.eclipse.userstorage/src/org/eclipse/userstorage/spi/StorageFactory.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Eike Stepper - initial API and implementation
- */
-package org.eclipse.userstorage.spi;
-
-import org.eclipse.userstorage.IStorage;
-import org.eclipse.userstorage.IStorageService.Registry;
-import org.eclipse.userstorage.internal.DefaultStorageFactory;
-import org.eclipse.userstorage.internal.InternalStorageFactory;
-import org.eclipse.userstorage.util.BadApplicationTokenException;
-
-import java.util.NoSuchElementException;
-
-/**
- * Creates {@link IStorage storages}.
- *
- * @author Eike Stepper
- */
-public class StorageFactory extends InternalStorageFactory
-{
- public static final StorageFactory DEFAULT = new DefaultStorageFactory();
-
- /**
- * Constructs this storage factory.
- */
- public StorageFactory()
- {
- }
-
- /**
- * Creates a storage for the application identified by the given application token.
- * <p>
- * Calling this method is identical to calling <code>create(applicationToken, null)</code>.
- * <p>
- *
- * @param applicationToken the application token that identifies the application of the storage to be created.
- * Minimal {@link BadApplicationTokenException#validate(String) lexical validation} is performed on the passed application token.<p>
- * @return the newly created storage, never <code>null</code>.<p>
- * @throws NoSuchElementException if the {@link Registry storage registry} is empty and, hence, there is no default storage available.<p>
- * @throws BadApplicationTokenException if {@link BadApplicationTokenException#validate(String) lexical validation} of the passed application token fails.<p>
- *
- * @see #create(String, StorageCache)
- */
- public final IStorage create(String applicationToken) throws NoSuchElementException, BadApplicationTokenException
- {
- return super.create(applicationToken, null);
- }
-
- /**
- * Creates a storage for the application identified by the given application token and associates it with a given {@link StorageCache storage cache}.
- * <p>
- * @param applicationToken the application token that identifies the application of the storage to be created.
- * Minimal {@link BadApplicationTokenException#validate(String) lexical validation} is performed on the passed application token.<p>
- * @param cache a local storage cache to be used as a locally persistent optimization, or <code>null</code> if local caching is not wanted.<p>
- * @return the newly created storage, never <code>null</code>.<p>
- * @throws NoSuchElementException if the {@link Registry storage registry} is empty and, hence, there is no default storage available.<p>
- * @throws BadApplicationTokenException if {@link BadApplicationTokenException#validate(String) lexical validation} of the passed application token fails.<p>
- *
- * @see #create(String)
- * @see StorageCache
- */
- @Override
- public final IStorage create(String applicationToken, StorageCache cache) throws NoSuchElementException, BadApplicationTokenException
- {
- return super.create(applicationToken, cache);
- }
-
- /**
- * Returns the URI of the preferred service for the given application.
- * <p>
- * Subclasses can override this method, for example, to implement a per-application service memory.
- * The default implementation returns <code>null</code>.
- * <p>
- *
- * @param applicationToken the application token.<p>
- * @return the URI of the preferred service for the given application.
- */
- @Override
- protected String getPreferredServiceURI(String applicationToken)
- {
- return null;
- }
-
- /**
- * Sets the URI of the preferred service for the given application.
- * <p>
- * Subclasses can override this method, for example, to implement a per-application service memory.
- * The default implementation does nothing.
- * <p>
- *
- * @param applicationToken the application token.<p>
- * @param serviceURI the URI of the preferred service for the given application.
- */
- @Override
- protected void setPreferredServiceURI(String applicationToken, String serviceURI)
- {
- // Do nothing.
- }
-}
diff --git a/org.eclipse.userstorage/src/org/eclipse/userstorage/util/Settings.java b/org.eclipse.userstorage/src/org/eclipse/userstorage/util/Settings.java
new file mode 100644
index 0000000..b1c978a
--- /dev/null
+++ b/org.eclipse.userstorage/src/org/eclipse/userstorage/util/Settings.java
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Eike Stepper - initial API and implementation
+ */
+package org.eclipse.userstorage.util;
+
+import org.eclipse.userstorage.internal.Activator;
+import org.eclipse.userstorage.internal.StorageProperties;
+import org.eclipse.userstorage.internal.util.IOUtil;
+import org.eclipse.userstorage.spi.ISettings;
+
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.preferences.IEclipsePreferences;
+
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author Eike Stepper
+ */
+public final class Settings
+{
+ public static final ISettings DEFAULT = createDefaultSettings();
+
+ private Settings()
+ {
+ }
+
+ private static ISettings createDefaultSettings()
+ {
+ String property = System.getProperty(StorageProperties.SETTINGS, null);
+ if (property != null)
+ {
+ try
+ {
+ @SuppressWarnings("unchecked")
+ Class<ISettings> c = (Class<ISettings>)Class.forName(property);
+ return c.newInstance();
+ }
+ catch (Throwable ex)
+ {
+ Activator.log(ex);
+ }
+ }
+
+ if (Activator.PLATFORM_RUNNING)
+ {
+ try
+ {
+ return new EclipseSettings("instance");
+ }
+ catch (Throwable ex)
+ {
+ //$FALL-THROUGH$
+ }
+
+ try
+ {
+ return new EclipseSettings("configuration");
+ }
+ catch (Throwable ex)
+ {
+ //$FALL-THROUGH$
+ }
+ }
+
+ return new MemorySettings();
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class NoSettings implements ISettings
+ {
+ public NoSettings()
+ {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getValue(String key) throws Exception
+ {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setValue(String key, String value) throws Exception
+ {
+ // Do nothing.
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class MemorySettings implements ISettings
+ {
+ private final Map<String, String> map = new HashMap<String, String>();
+
+ public MemorySettings()
+ {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getValue(String key) throws Exception
+ {
+ return map.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setValue(String key, String value) throws Exception
+ {
+ if (value == null)
+ {
+ map.remove(key);
+ }
+ else
+ {
+ map.put(key, value);
+ }
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public static final class EclipseSettings implements ISettings
+ {
+ private final Preferences node;
+
+ public EclipseSettings(String scope) throws BackingStoreException
+ {
+ IEclipsePreferences rootNode = Platform.getPreferencesService().getRootNode();
+ if (!rootNode.nodeExists(scope))
+ {
+ throw new BackingStoreException("Invalid scope: " + scope);
+ }
+
+ String nodeName = getNodeName();
+ node = rootNode.node(scope).node(nodeName);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getValue(String key) throws Exception
+ {
+ return node.get(key, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setValue(String key, String value) throws Exception
+ {
+ if (value == null)
+ {
+ node.remove(key);
+ }
+ else
+ {
+ node.put(key, value);
+ }
+
+ node.flush();
+ }
+
+ protected String getNodeName()
+ {
+ return Activator.PLUGIN_ID;
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ public final class FileSettings implements ISettings
+ {
+ private final File file;
+
+ public FileSettings(File file)
+ {
+ this.file = file;
+ }
+
+ public FileSettings()
+ {
+ this(new File(System.getProperty("user.home"), ".eclipse/" + Activator.PLUGIN_ID + "/.settings"));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getValue(String key) throws Exception
+ {
+ Properties properties = new Properties();
+
+ if (file.isFile())
+ {
+ InputStream in = null;
+
+ try
+ {
+ in = new FileInputStream(file);
+ properties.load(in);
+ }
+ finally
+ {
+ IOUtil.close(in);
+ }
+ }
+
+ return properties.getProperty(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setValue(String key, String value) throws Exception
+ {
+ Properties properties = new Properties();
+
+ if (file.isFile())
+ {
+ InputStream in = null;
+
+ try
+ {
+ in = new FileInputStream(file);
+ properties.load(in);
+ }
+ finally
+ {
+ IOUtil.close(in);
+ }
+ }
+
+ boolean changed = false;
+ if (value == null)
+ {
+ Object oldValue = properties.remove(key);
+ if (oldValue != null)
+ {
+ changed = true;
+ }
+ }
+ else
+ {
+ Object oldValue = properties.setProperty(key, value);
+ if (oldValue != null && !oldValue.equals(value))
+ {
+ changed = true;
+ }
+ }
+
+ if (changed)
+ {
+ OutputStream out = null;
+
+ try
+ {
+ out = new FileOutputStream(file);
+ properties.store(out, null);
+ }
+ finally
+ {
+ IOUtil.close(out);
+ }
+ }
+ }
+ }
+}