blob: 6d98cbf2e3870c97705bcd4839161a6b592c2b8f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2010 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.internal.preferences.legacy;
import java.io.*;
import java.util.Iterator;
import java.util.Properties;
import org.eclipse.core.internal.preferences.*;
import org.eclipse.core.internal.runtime.RuntimeLog;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.osgi.service.prefs.BackingStoreException;
/**
* This class represents a convenience layer between the Eclipse 3.0
* preferences and pre-3.0 preferences. It acts as a bridge between the
* org.eclipse.core.runtime.Preferences object associated with a particular plug-in
* object, and its corresponding preference node in the 3.0 preference node
* hierarchy.
*
* @since 3.0
*/
public class PreferenceForwarder extends Preferences implements IEclipsePreferences.IPreferenceChangeListener, IEclipsePreferences.INodeChangeListener {
private static final byte[] BYTE_ARRAY_DEFAULT_DEFAULT = new byte[0];
private IEclipsePreferences pluginRoot = (IEclipsePreferences) PreferencesService.getDefault().getRootNode().node(InstanceScope.SCOPE);
private DefaultPreferences defaultsRoot = (DefaultPreferences) PreferencesService.getDefault().getRootNode().node(DefaultScope.SCOPE);
private String pluginID;
private Object plugin;
// boolean to check to see if we should re-wrap and forward change
// events coming from the new runtime APIs.
private boolean notify = true;
/*
* Used for test suites only.
*/
public PreferenceForwarder(String pluginID) {
this(null, pluginID);
}
public PreferenceForwarder(Object plugin, String pluginID) {
super();
this.plugin = plugin;
this.pluginID = pluginID;
}
/*
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener#added(org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent)
*/
public synchronized void added(IEclipsePreferences.NodeChangeEvent event) {
if (listeners.size() > 0 && pluginID.equals(event.getChild().name())) {
try {
EclipsePreferences prefs = (EclipsePreferences) event.getChild();
prefs.addPreferenceChangeListener(this);
} catch (ClassCastException e) {
throw new RuntimeException("Plug-in preferences must be instances of EclipsePreferences: " + e.getMessage()); //$NON-NLS-1$
}
}
}
/*
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener#removed(org.eclipse.core.runtime.preferences.IEclipsePreferences.NodeChangeEvent)
*/
public synchronized void removed(IEclipsePreferences.NodeChangeEvent event) {
// Do nothing. We can't remove ourselves from the node's list of preference change
// listeners because the node has already been removed.
}
/**
* Adds a property change listener to this preference object.
* Has no effect if the identical listener is already registered.
*
* @param listener a property change listener
*/
public synchronized void addPropertyChangeListener(IPropertyChangeListener listener) {
if (listeners.size() == 0) {
EclipsePreferences prefs = getPluginPreferences(false);
if (prefs != null) {
prefs.addPreferenceChangeListener(this);
}
pluginRoot.addNodeChangeListener(this);
}
listeners.add(listener);
}
/*
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
*/
public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) {
// if we are the ones making this change, then don't broadcast
if (!notify)
return;
Object oldValue = event.getOldValue();
Object newValue = event.getNewValue();
String key = event.getKey();
if (newValue == null)
newValue = getDefault(key, oldValue);
else if (oldValue == null)
oldValue = getDefault(key, newValue);
firePropertyChangeEvent(key, oldValue, newValue);
}
private EclipsePreferences getPluginPreferences(boolean create) {
try {
if (!create && !pluginRoot.nodeExists(pluginID))
return null;
} catch (BackingStoreException e) {
return null;
}
try {
return (EclipsePreferences) pluginRoot.node(pluginID);
} catch (ClassCastException e) {
throw new RuntimeException("Plug-in preferences must be instances of EclipsePreferences: " + e.getMessage()); //$NON-NLS-1$
}
}
private IEclipsePreferences getDefaultPreferences() {
return defaultsRoot.node(pluginID, plugin);
}
/**
* Removes the given listener from this preference object.
* Has no effect if the listener is not registered.
*
* @param listener a property change listener
*/
public synchronized void removePropertyChangeListener(IPropertyChangeListener listener) {
listeners.remove(listener);
if (listeners.size() == 0) {
EclipsePreferences prefs = getPluginPreferences(false);
if (prefs != null) {
prefs.removePreferenceChangeListener(this);
}
pluginRoot.removeNodeChangeListener(this);
}
}
/**
* Does its best at determining the default value for the given key. Checks the
* given object's type and then looks in the list of defaults to see if a value
* exists. If not or if there is a problem converting the value, the default default
* value for that type is returned.
*/
private Object getDefault(String key, Object obj) {
IEclipsePreferences defaults = getDefaultPreferences();
if (obj instanceof String)
return defaults.get(key, STRING_DEFAULT_DEFAULT);
else if (obj instanceof Integer)
return new Integer(defaults.getInt(key, INT_DEFAULT_DEFAULT));
else if (obj instanceof Double)
return new Double(defaults.getDouble(key, DOUBLE_DEFAULT_DEFAULT));
else if (obj instanceof Float)
return new Float(defaults.getFloat(key, FLOAT_DEFAULT_DEFAULT));
else if (obj instanceof Long)
return new Long(defaults.getLong(key, LONG_DEFAULT_DEFAULT));
else if (obj instanceof byte[])
return defaults.getByteArray(key, BYTE_ARRAY_DEFAULT_DEFAULT);
else if (obj instanceof Boolean)
return defaults.getBoolean(key, BOOLEAN_DEFAULT_DEFAULT) ? Boolean.TRUE : Boolean.FALSE;
else
return null;
}
/**
* Returns whether the given property is known to this preference object,
* either by having an explicit setting or by having a default
* setting.
*
* @param name the name of the property
* @return <code>true</code> if either a current value or a default
* value is known for the named property, and <code>false</code>otherwise
*/
public boolean contains(String name) {
if (name == null)
return false;
String value = getPluginPreferences(true).get(name, null);
if (value != null)
return true;
return getDefaultPreferences().get(name, null) != null;
}
/**
* Returns the current value of the boolean-valued property with the
* given name.
* Returns the default-default value (<code>false</code>) if there
* is no property with the given name, or if the current value
* cannot be treated as a boolean.
*
* @param name the name of the property
* @return the boolean-valued property
*/
public boolean getBoolean(String name) {
return getPluginPreferences(true).getBoolean(name, getDefaultPreferences().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the boolean-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property
*/
public void setValue(String name, boolean value) {
Boolean oldValue = getBoolean(name) ? Boolean.TRUE : Boolean.FALSE;
Boolean newValue = value ? Boolean.TRUE : Boolean.FALSE;
if (newValue == oldValue)
return;
try {
notify = false;
if (getDefaultBoolean(name) == value)
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).putBoolean(name, value);
firePropertyChangeEvent(name, oldValue, newValue);
} finally {
notify = true;
}
}
/**
* Returns the default value for the boolean-valued property
* with the given name.
* Returns the default-default value (<code>false</code>) if there
* is no default property with the given name, or if the default
* value cannot be treated as a boolean.
*
* @param name the name of the property
* @return the default value of the named property
*/
public boolean getDefaultBoolean(String name) {
return getDefaultPreferences().getBoolean(name, BOOLEAN_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the boolean-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property
*/
public void setDefault(String name, boolean value) {
getDefaultPreferences().putBoolean(name, value);
}
/**
* Returns the current value of the double-valued property with the
* given name.
* Returns the default-default value (<code>0.0</code>) if there
* is no property with the given name, or if the current value
* cannot be treated as a double.
*
* @param name the name of the property
* @return the double-valued property
*/
public double getDouble(String name) {
return getPluginPreferences(true).getDouble(name, getDefaultPreferences().getDouble(name, DOUBLE_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the double-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property; must be
* a number (not a NaN)
*/
public void setValue(String name, double value) {
if (Double.isNaN(value))
throw new IllegalArgumentException();
final double doubleValue = getDouble(name);
if (value == doubleValue)
return;
Double oldValue = new Double(doubleValue);
Double newValue = new Double(value);
try {
notify = false;
if (getDefaultDouble(name) == value)
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).putDouble(name, value);
firePropertyChangeEvent(name, oldValue, newValue);
} finally {
notify = true;
}
}
/**
* Returns the default value for the double-valued property
* with the given name.
* Returns the default-default value (<code>0.0</code>) if there
* is no default property with the given name, or if the default
* value cannot be treated as a double.
*
* @param name the name of the property
* @return the default value of the named property
*/
public double getDefaultDouble(String name) {
return getDefaultPreferences().getDouble(name, DOUBLE_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the double-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property; must be
* a number (not a NaN)
*/
public void setDefault(String name, double value) {
if (Double.isNaN(value))
throw new IllegalArgumentException();
getDefaultPreferences().putDouble(name, value);
}
/**
* Returns the current value of the float-valued property with the
* given name.
* Returns the default-default value (<code>0.0f</code>) if there
* is no property with the given name, or if the current value
* cannot be treated as a float.
*
* @param name the name of the property
* @return the float-valued property
*/
public float getFloat(String name) {
return getPluginPreferences(true).getFloat(name, getDefaultPreferences().getFloat(name, FLOAT_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the float-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property; must be
* a number (not a NaN)
*/
public void setValue(String name, float value) {
if (Float.isNaN(value))
throw new IllegalArgumentException();
final float floatValue = getFloat(name);
if (value == floatValue)
return;
Float oldValue = new Float(floatValue);
Float newValue = new Float(value);
try {
notify = false;
if (getDefaultFloat(name) == value)
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).putFloat(name, value);
firePropertyChangeEvent(name, oldValue, newValue);
} finally {
notify = true;
}
}
/**
* Returns the default value for the float-valued property
* with the given name.
* Returns the default-default value (<code>0.0f</code>) if there
* is no default property with the given name, or if the default
* value cannot be treated as a float.
*
* @param name the name of the property
* @return the default value of the named property
*/
public float getDefaultFloat(String name) {
return getDefaultPreferences().getFloat(name, FLOAT_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the float-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property; must be
* a number (not a NaN)
*/
public void setDefault(String name, float value) {
if (Float.isNaN(value))
throw new IllegalArgumentException();
getDefaultPreferences().putFloat(name, value);
}
/**
* Returns the current value of the integer-valued property with the
* given name.
* Returns the default-default value (<code>0</code>) if there
* is no property with the given name, or if the current value
* cannot be treated as an integter.
*
* @param name the name of the property
* @return the int-valued property
*/
public int getInt(String name) {
return getPluginPreferences(true).getInt(name, getDefaultPreferences().getInt(name, INT_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the integer-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property
*/
public void setValue(String name, int value) {
final int intValue = getInt(name);
if (value == intValue)
return;
Integer oldValue = new Integer(intValue);
Integer newValue = new Integer(value);
try {
notify = false;
if (getDefaultInt(name) == value)
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).putInt(name, value);
firePropertyChangeEvent(name, oldValue, newValue);
} finally {
notify = true;
}
}
/**
* Returns the default value for the integer-valued property
* with the given name.
* Returns the default-default value (<code>0</code>) if there
* is no default property with the given name, or if the default
* value cannot be treated as an integer.
*
* @param name the name of the property
* @return the default value of the named property
*/
public int getDefaultInt(String name) {
return getDefaultPreferences().getInt(name, INT_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the integer-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property
*/
public void setDefault(String name, int value) {
getDefaultPreferences().putInt(name, value);
}
/**
* Returns the current value of the long-valued property with the
* given name.
* Returns the default-default value (<code>0L</code>) if there
* is no property with the given name, or if the current value
* cannot be treated as a long.
*
* @param name the name of the property
* @return the long-valued property
*/
public long getLong(String name) {
return getPluginPreferences(true).getLong(name, getDefaultPreferences().getLong(name, LONG_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the long-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property
*/
public void setValue(String name, long value) {
final long longValue = getLong(name);
if (value == longValue)
return;
Long oldValue = new Long(longValue);
Long newValue = new Long(value);
try {
notify = false;
if (getDefaultLong(name) == value)
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).putLong(name, value);
firePropertyChangeEvent(name, oldValue, newValue);
} finally {
notify = true;
}
}
/**
* Returns the default value for the long-valued property
* with the given name.
* Returns the default-default value (<code>0L</code>) if there
* is no default property with the given name, or if the default
* value cannot be treated as a long.
*
* @param name the name of the property
* @return the default value of the named property
*/
public long getDefaultLong(String name) {
return getDefaultPreferences().getLong(name, LONG_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the long-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property
*/
public void setDefault(String name, long value) {
getDefaultPreferences().putLong(name, value);
}
/**
* Returns the current value of the string-valued property with the
* given name.
* Returns the default-default value (the empty string <code>""</code>)
* if there is no property with the given name.
*
* @param name the name of the property
* @return the string-valued property
*/
public String getString(String name) {
return getPluginPreferences(true).get(name, getDefaultPreferences().get(name, STRING_DEFAULT_DEFAULT));
}
/**
* Sets the current value of the string-valued property with the
* given name.
* <p>
* A property change event is reported if the current value of the
* property actually changes from its previous value. In the event
* object, the property name is the name of the property, and the
* old and new values are wrapped as objects.
* </p>
* <p>
* If the given value is the same as the corresponding default value
* for the given property, the explicit setting is deleted.
* Note that the recommended way of re-initializing a property to its
* default value is to call <code>setToDefault</code>.
* </p>
*
* @param name the name of the property
* @param value the new current value of the property
*/
public void setValue(String name, String value) {
if (value == null)
throw new IllegalArgumentException();
String oldValue = getString(name);
if (value.equals(oldValue))
return;
try {
notify = false;
if (getDefaultString(name).equals(value))
getPluginPreferences(true).remove(name);
else
getPluginPreferences(true).put(name, value);
firePropertyChangeEvent(name, oldValue, value);
} finally {
notify = true;
}
}
/**
* Returns the default value for the string-valued property
* with the given name.
* Returns the default-default value (the empty string <code>""</code>)
* is no default property with the given name, or if the default
* value cannot be treated as a string.
*
* @param name the name of the property
* @return the default value of the named property
*/
public String getDefaultString(String name) {
return getDefaultPreferences().get(name, STRING_DEFAULT_DEFAULT);
}
/**
* Sets the default value for the string-valued property with the
* given name.
* <p>
* Note that the current value of the property is affected if
* the property's current value was its old default value, in which
* case it changes to the new default value. If the property's current
* is different from its old default value, its current value is
* unaffected. No property change events are reported by changing default
* values.
* </p>
*
* @param name the name of the property
* @param value the new default value for the property
*/
public void setDefault(String name, String value) {
if (value == null)
throw new IllegalArgumentException();
getDefaultPreferences().put(name, value);
}
/**
* Returns whether the property with the given name has the default value in
* virtue of having no explicitly set value.
*
* @param name the name of the property
* @return <code>true</code> if the property has no explicitly set value,
* and <code>false</code> otherwise (including the case where the property
* is unknown to this object)
*/
public boolean isDefault(String name) {
if (name == null)
return false;
return getPluginPreferences(true).get(name, null) == null;
}
/**
* Sets the current value of the property with the given name back
* to its default value. Has no effect if the property does not have
* its own current value.
* <p>
* Note that the recommended way of re-initializing a property to the
* appropriate default value is to call <code>setToDefault</code>.
* This is implemented by removing the named value from the object,
* thereby exposing the default value.
* </p>
* <p>
* A property change event is always reported. In the event
* object, the property name is the name of the property, and the
* old and new values are either strings, or <code>null</code>
* indicating the default-default value.
* </p>
*
* @param name the name of the property
*/
public void setToDefault(String name) {
IEclipsePreferences preferences = getPluginPreferences(true);
Object oldValue = preferences.get(name, null);
if (oldValue != null)
preferences.remove(name);
}
/**
* Returns a list of all properties known to this preference object which
* have current values other than their default value.
*
* @return an array of property names
*/
public String[] propertyNames() {
return getPluginPreferences(true).keys();
}
/**
* Returns a list of all properties known to this preference object which
* have default values other than their default-default value.
*
* @return an array of property names
*/
public String[] defaultPropertyNames() {
try {
return getDefaultPreferences().keys();
} catch (BackingStoreException e) {
logError(e.getMessage(), e);
return new String[0];
}
}
/**
* Returns whether the current values in this preference object
* require saving.
*
* @return <code>true</code> if at least one of the properties
* known to this preference object has a current value different from its
* default value, and <code>false</code> otherwise
*/
public boolean needsSaving() {
return getPluginPreferences(true).isDirty();
}
/**
* Flush the values of these plug-in preferences to disk.
*
* @throws BackingStoreException
*/
public void flush() throws BackingStoreException {
IEclipsePreferences node = getPluginPreferences(false);
if (node != null)
node.flush();
}
/*
* Something bad happened so log it.
*/
private void logError(String message, Exception e) {
IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
RuntimeLog.log(status);
}
/*
* @see org.eclipse.core.runtime.Preferences#load(java.io.InputStream)
*/
public void load(InputStream in) throws IOException {
Properties result = new Properties();
result.load(in);
convertFromProperties(result);
// We loaded the prefs from a non-default location so now
// store them to disk. This also clears the dirty flag
// and therefore sets the #needsSaving() state correctly.
try {
flush();
} catch (BackingStoreException e) {
throw new IOException(e.getMessage());
}
}
/*
* @see org.eclipse.core.runtime.Preferences#store(java.io.OutputStream, java.lang.String)
*/
public void store(OutputStream out, String header) throws IOException {
Properties result = convertToProperties();
result.store(out, header);
// We stored the prefs to a non-default location but the spec
// says that the dirty state is cleared so we want to store
// them to disk at the default location as well.
try {
flush();
} catch (BackingStoreException e) {
throw new IOException(e.getMessage());
}
}
private void convertFromProperties(Properties props) {
IEclipsePreferences preferences = getPluginPreferences(true);
for (Iterator i = props.keySet().iterator(); i.hasNext();) {
String key = (String) i.next();
String value = props.getProperty(key);
if (value != null)
preferences.put(key, value);
}
}
public String toString() {
return "PreferenceForwarder(" + pluginID + ")"; //$NON-NLS-1$ //$NON-NLS-2$
}
/*
* Convert the preferences in this node to a properties file
* suitable for persistence.
*/
private Properties convertToProperties() {
Properties result = new Properties();
String[] keys = propertyNames();
for (int i = 0; i < keys.length; i++) {
String key = keys[i];
String value = getString(key);
if (!Preferences.STRING_DEFAULT_DEFAULT.equals(value))
result.put(key, value);
}
return result;
}
}