blob: 384c5192274f13dee9527cb8bc46867e0ad74cab [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2006 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.ui.internal.preferences;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.commands.common.EventManager;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IPreferenceNodeVisitor;
import org.osgi.service.prefs.BackingStoreException;
import org.osgi.service.prefs.Preferences;
/**
* Represents a working copy of a preference node, backed by the real node.
* <p>
* Note: Working copy nodes do not fire node change events.
* </p>
* <p>
* Note: Preference change listeners registered on this node will only receive
* events from this node and not events based on the original backing node.
* </p>
* @since 3.1
*/
public class WorkingCopyPreferences extends EventManager implements
IEclipsePreferences {
private static final String TRUE = "true"; //$NON-NLS-1$
private final Map temporarySettings;
private final IEclipsePreferences original;
private boolean removed = false;
private org.eclipse.ui.preferences.WorkingCopyManager manager;
/**
* @param original the underlying preference node
* @param manager the working copy manager
*/
public WorkingCopyPreferences(IEclipsePreferences original, org.eclipse.ui.preferences.WorkingCopyManager manager) {
super();
this.original = original;
this.manager = manager;
this.temporarySettings = new HashMap();
}
/*
* Convenience method for throwing an exception when methods
* are called on a removed node.
*/
private void checkRemoved() {
if (removed) {
String message = "Preference node: " + absolutePath() + " has been removed."; //$NON-NLS-1$ //$NON-NLS-2$
throw new IllegalStateException(message);
}
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences#addNodeChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener)
*/
public void addNodeChangeListener(INodeChangeListener listener) {
// no-op - working copy nodes don't fire node change events
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences#removeNodeChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener)
*/
public void removeNodeChangeListener(INodeChangeListener listener) {
// no-op - working copy nodes don't fire node change events
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences#addPreferenceChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener)
*/
public void addPreferenceChangeListener(IPreferenceChangeListener listener) {
checkRemoved();
addListenerObject(listener);
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences#removePreferenceChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener)
*/
public void removePreferenceChangeListener(IPreferenceChangeListener listener) {
checkRemoved();
removeListenerObject(listener);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#removeNode()
*/
public void removeNode() throws BackingStoreException {
checkRemoved();
// clear all values (long way so people get notified)
String[] keys = keys();
for (int i = 0; i < keys.length; i++) {
remove(keys[i]);
}
// remove children
String[] childNames = childrenNames();
for (int i = 0; i < childNames.length; i++) {
node(childNames[i]).removeNode();
}
// mark as removed
removed = true;
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#node(java.lang.String)
*/
public Preferences node(String path) {
checkRemoved();
return manager.getWorkingCopy((IEclipsePreferences) getOriginal().node(path));
}
/* (non-Javadoc)
* @see org.eclipse.core.runtime.preferences.IEclipsePreferences#accept(org.eclipse.core.runtime.preferences.IPreferenceNodeVisitor)
*/
public void accept(IPreferenceNodeVisitor visitor) throws BackingStoreException {
checkRemoved();
if (!visitor.visit(this)) {
return;
}
String[] childNames = childrenNames();
for (int i = 0; i < childNames.length; i++) {
((IEclipsePreferences) node(childNames[i])).accept(visitor);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#put(java.lang.String, java.lang.String)
*/
public void put(String key, String value) {
checkRemoved();
if (key == null || value == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
temporarySettings.put(key, value);
if (!value.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, value);
}
}
private void firePropertyChangeEvent(String key, Object oldValue, Object newValue) {
Object[] listeners = getListeners();
if (listeners.length == 0) {
return;
}
PreferenceChangeEvent event = new PreferenceChangeEvent(this, key, oldValue, newValue);
for (int i = 0; i < listeners.length; i++) {
((IPreferenceChangeListener) listeners[i]).preferenceChange(event);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#get(java.lang.String, java.lang.String)
*/
public String get(String key, String defaultValue) {
checkRemoved();
return internalGet(key, defaultValue);
}
private String internalGet(String key, String defaultValue) {
if (key == null) {
throw new NullPointerException();
}
if (temporarySettings.containsKey(key)) {
Object value = temporarySettings.get(key);
return value == null ? defaultValue : (String) value;
}
return getOriginal().get(key, defaultValue);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#remove(java.lang.String)
*/
public void remove(String key) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
Object oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = temporarySettings.get(key);
} else {
oldValue = original.get(key, null);
}
if (oldValue == null) {
return;
}
temporarySettings.put(key, null);
firePropertyChangeEvent(key, oldValue, null);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#clear()
*/
public void clear() {
checkRemoved();
for (Iterator i = temporarySettings.keySet().iterator(); i.hasNext();) {
String key = (String) i.next();
Object value = temporarySettings.get(key);
if (value != null) {
temporarySettings.put(key, null);
firePropertyChangeEvent(key, value, null);
}
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putInt(java.lang.String, int)
*/
public void putInt(String key, int value) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = Integer.toString(value);
temporarySettings.put(key, newValue);
if (!newValue.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getInt(java.lang.String, int)
*/
public int getInt(String key, int defaultValue) {
checkRemoved();
String value = internalGet(key, null);
int result = defaultValue;
if (value != null) {
try {
result = Integer.parseInt(value);
} catch (NumberFormatException e) {
// use default
}
}
return result;
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putLong(java.lang.String, long)
*/
public void putLong(String key, long value) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = Long.toString(value);
temporarySettings.put(key, newValue);
if (!newValue.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getLong(java.lang.String, long)
*/
public long getLong(String key, long defaultValue) {
checkRemoved();
String value = internalGet(key, null);
long result = defaultValue;
if (value != null) {
try {
result = Long.parseLong(value);
} catch (NumberFormatException e) {
// use default
}
}
return result;
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putBoolean(java.lang.String, boolean)
*/
public void putBoolean(String key, boolean value) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = String.valueOf(value);
temporarySettings.put(key, newValue);
if (!newValue.equalsIgnoreCase(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getBoolean(java.lang.String, boolean)
*/
public boolean getBoolean(String key, boolean defaultValue) {
checkRemoved();
String value = internalGet(key, null);
return value == null ? defaultValue : TRUE.equalsIgnoreCase(value);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putFloat(java.lang.String, float)
*/
public void putFloat(String key, float value) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = Float.toString(value);
temporarySettings.put(key, newValue);
if (!newValue.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getFloat(java.lang.String, float)
*/
public float getFloat(String key, float defaultValue) {
checkRemoved();
String value = internalGet(key, null);
float result = defaultValue;
if (value != null) {
try {
result = Float.parseFloat(value);
} catch (NumberFormatException e) {
// use default
}
}
return result;
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putDouble(java.lang.String, double)
*/
public void putDouble(String key, double value) {
checkRemoved();
if (key == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = Double.toString(value);
temporarySettings.put(key, newValue);
if (!newValue.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getDouble(java.lang.String, double)
*/
public double getDouble(String key, double defaultValue) {
checkRemoved();
String value = internalGet(key, null);
double result = defaultValue;
if (value != null) {
try {
result = Double.parseDouble(value);
} catch (NumberFormatException e) {
// use default
}
}
return result;
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#putByteArray(java.lang.String, byte[])
*/
public void putByteArray(String key, byte[] value) {
checkRemoved();
if (key == null || value == null) {
throw new NullPointerException();
}
String oldValue = null;
if (temporarySettings.containsKey(key)) {
oldValue = (String) temporarySettings.get(key);
} else {
oldValue = getOriginal().get(key, null);
}
String newValue = new String(Base64.encode(value));
temporarySettings.put(key, newValue);
if (!newValue.equals(oldValue)) {
firePropertyChangeEvent(key, oldValue, newValue);
}
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#getByteArray(java.lang.String, byte[])
*/
public byte[] getByteArray(String key, byte[] defaultValue) {
checkRemoved();
String value = internalGet(key, null);
return value == null ? defaultValue : Base64.decode(value.getBytes());
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#keys()
*/
public String[] keys() throws BackingStoreException {
checkRemoved();
HashSet allKeys = new HashSet(Arrays.asList(getOriginal().keys()));
allKeys.addAll(temporarySettings.keySet());
return (String[]) allKeys.toArray(new String[allKeys.size()]);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#childrenNames()
*/
public String[] childrenNames() throws BackingStoreException {
checkRemoved();
return getOriginal().childrenNames();
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#parent()
*/
public Preferences parent() {
checkRemoved();
return manager.getWorkingCopy((IEclipsePreferences) getOriginal().parent());
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#nodeExists(java.lang.String)
*/
public boolean nodeExists(String pathName) throws BackingStoreException {
// short circuit for this node
if (pathName.length() == 0) {
return removed ? false : getOriginal().nodeExists(pathName);
}
return getOriginal().nodeExists(pathName);
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#name()
*/
public String name() {
return getOriginal().name();
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#absolutePath()
*/
public String absolutePath() {
return getOriginal().absolutePath();
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#flush()
*/
public void flush() throws BackingStoreException {
if (removed) {
getOriginal().removeNode();
return;
}
checkRemoved();
// update underlying preferences
for (Iterator i = temporarySettings.keySet().iterator(); i.hasNext();) {
String key = (String) i.next();
String value = (String) temporarySettings.get(key);
if (value == null) {
getOriginal().remove(key);
} else {
getOriginal().put(key, value);
}
}
// clear our settings
temporarySettings.clear();
// save the underlying preference store
getOriginal().flush();
}
/* (non-Javadoc)
* @see org.osgi.service.prefs.Preferences#sync()
*/
public void sync() throws BackingStoreException {
checkRemoved();
// forget our settings
temporarySettings.clear();
// load the underlying preference store
getOriginal().sync();
}
/**
* @return Returns the original preference node.
*/
private IEclipsePreferences getOriginal() {
return original;
}
}