| /******************************************************************************* |
| * 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; |
| } |
| } |