Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--bundles/org.eclipse.equinox.preferences/.settings/org.eclipse.pde.api.tools.prefs98
-rw-r--r--bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/EclipsePreferences.java2472
2 files changed, 1336 insertions, 1234 deletions
diff --git a/bundles/org.eclipse.equinox.preferences/.settings/org.eclipse.pde.api.tools.prefs b/bundles/org.eclipse.equinox.preferences/.settings/org.eclipse.pde.api.tools.prefs
new file mode 100644
index 000000000..7e99fe9e5
--- /dev/null
+++ b/bundles/org.eclipse.equinox.preferences/.settings/org.eclipse.pde.api.tools.prefs
@@ -0,0 +1,98 @@
+#Tue Aug 23 08:47:58 EDT 2011
+ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Error
+ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Error
+ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_API_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_REEXPORTED_TYPE=Error
+API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Error
+API_USE_SCAN_FIELD_SEVERITY=Error
+API_USE_SCAN_METHOD_SEVERITY=Error
+API_USE_SCAN_TYPE_SEVERITY=Error
+CLASS_ELEMENT_TYPE_ADDED_METHOD=Error
+CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+CLASS_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+CLASS_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+CLASS_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+CLASS_ELEMENT_TYPE_REMOVED_CONSTRUCTOR=Error
+CLASS_ELEMENT_TYPE_REMOVED_FIELD=Error
+CLASS_ELEMENT_TYPE_REMOVED_METHOD=Error
+CLASS_ELEMENT_TYPE_REMOVED_SUPERCLASS=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+CLASS_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+CONSTRUCTOR_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+CONSTRUCTOR_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+ENUM_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+ENUM_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+ENUM_ELEMENT_TYPE_REMOVED_ENUM_CONSTANT=Error
+ENUM_ELEMENT_TYPE_REMOVED_FIELD=Error
+ENUM_ELEMENT_TYPE_REMOVED_METHOD=Error
+ENUM_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+FIELD_ELEMENT_TYPE_ADDED_VALUE=Error
+FIELD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+FIELD_ELEMENT_TYPE_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+FIELD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+FIELD_ELEMENT_TYPE_CHANGED_TYPE=Error
+FIELD_ELEMENT_TYPE_CHANGED_VALUE=Error
+FIELD_ELEMENT_TYPE_REMOVED_TYPE_ARGUMENT=Error
+FIELD_ELEMENT_TYPE_REMOVED_VALUE=Error
+ILLEGAL_EXTEND=Warning
+ILLEGAL_IMPLEMENT=Warning
+ILLEGAL_INSTANTIATE=Warning
+ILLEGAL_OVERRIDE=Warning
+ILLEGAL_REFERENCE=Warning
+INTERFACE_ELEMENT_TYPE_ADDED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_SUPER_INTERFACE_WITH_METHODS=Error
+INTERFACE_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Error
+INTERFACE_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_FIELD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_METHOD=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Error
+INTERFACE_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+INVALID_JAVADOC_TAG=Ignore
+INVALID_REFERENCE_IN_SYSTEM_LIBRARIES=Error
+LEAK_EXTEND=Warning
+LEAK_FIELD_DECL=Warning
+LEAK_IMPLEMENT=Warning
+LEAK_METHOD_PARAM=Warning
+LEAK_METHOD_RETURN_TYPE=Warning
+METHOD_ELEMENT_TYPE_ADDED_RESTRICTIONS=Error
+METHOD_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Error
+METHOD_ELEMENT_TYPE_CHANGED_DECREASE_ACCESS=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_ABSTRACT_TO_ABSTRACT=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_FINAL_TO_FINAL=Error
+METHOD_ELEMENT_TYPE_CHANGED_NON_STATIC_TO_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_STATIC_TO_NON_STATIC=Error
+METHOD_ELEMENT_TYPE_CHANGED_VARARGS_TO_ARRAY=Error
+METHOD_ELEMENT_TYPE_REMOVED_ANNOTATION_DEFAULT_VALUE=Error
+METHOD_ELEMENT_TYPE_REMOVED_TYPE_PARAMETER=Error
+MISSING_EE_DESCRIPTIONS=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_ADDED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_CHANGED_INTERFACE_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_CLASS_BOUND=Error
+TYPE_PARAMETER_ELEMENT_TYPE_REMOVED_INTERFACE_BOUND=Error
+UNUSED_PROBLEM_FILTERS=Warning
+automatically_removed_unused_problem_filters=false
+eclipse.preferences.version=1
+incompatible_api_component_version=Error
+incompatible_api_component_version_include_major_without_breaking_change=Disabled
+incompatible_api_component_version_include_minor_without_api_change=Disabled
+invalid_since_tag_version=Error
+malformed_since_tag=Error
+missing_since_tag=Error
+report_api_breakage_when_major_version_incremented=Disabled
+report_resolution_errors_api_component=Warning
diff --git a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/EclipsePreferences.java b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/EclipsePreferences.java
index 59deb0b9f..f419200b2 100644
--- a/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/EclipsePreferences.java
+++ b/bundles/org.eclipse.equinox.preferences/src/org/eclipse/core/internal/preferences/EclipsePreferences.java
@@ -1,1234 +1,1238 @@
-/*******************************************************************************
- * Copyright (c) 2004, 2011 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
- * Julian Chen - fix for bug #92572, jclRM
- *******************************************************************************/
-package org.eclipse.core.internal.preferences;
-
-import java.io.*;
-import java.util.*;
-import org.eclipse.core.internal.runtime.RuntimeLog;
-import org.eclipse.core.runtime.*;
-import org.eclipse.core.runtime.preferences.*;
-import org.eclipse.osgi.util.NLS;
-import org.osgi.service.prefs.BackingStoreException;
-import org.osgi.service.prefs.Preferences;
-
-/**
- * Represents a node in the Eclipse preference node hierarchy. This class
- * is used as a default implementation/super class for those nodes which
- * belong to scopes which are contributed by the Platform.
- *
- * Implementation notes:
- *
- * - For thread safety, we always synchronize on the node object when writing
- * the children or properties fields. Must ensure we don't synchronize when calling
- * client code such as listeners.
- *
- * @since 3.0
- */
-public class EclipsePreferences implements IEclipsePreferences, IScope {
-
- public static final String DEFAULT_PREFERENCES_DIRNAME = ".settings"; //$NON-NLS-1$
- public static final String PREFS_FILE_EXTENSION = "prefs"; //$NON-NLS-1$
- protected static final IEclipsePreferences[] EMPTY_NODE_ARRAY = new IEclipsePreferences[0];
- protected static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final String FALSE = "false"; //$NON-NLS-1$
- private static final String TRUE = "true"; //$NON-NLS-1$
- protected static final String VERSION_KEY = "eclipse.preferences.version"; //$NON-NLS-1$
- protected static final String VERSION_VALUE = "1"; //$NON-NLS-1$
- protected static final String PATH_SEPARATOR = String.valueOf(IPath.SEPARATOR);
- protected static final String DOUBLE_SLASH = "//"; //$NON-NLS-1$
- protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
-
- private String cachedPath;
- protected Map children;
- protected boolean dirty = false;
- protected boolean loading = false;
- protected final String name;
- // the parent of an EclipsePreference node is always an EclipsePreference node. (or null)
- protected final EclipsePreferences parent;
- protected ImmutableMap properties = ImmutableMap.EMPTY;
- protected boolean removed = false;
- private ListenerList nodeChangeListeners;
- private ListenerList preferenceChangeListeners;
-
- public static boolean DEBUG_PREFERENCE_GENERAL = false;
- public static boolean DEBUG_PREFERENCE_SET = false;
- public static boolean DEBUG_PREFERENCE_GET = false;
-
- protected final static String debugPluginName = "org.eclipse.equinox.preferences"; //$NON-NLS-1$
-
- static {
- DEBUG_PREFERENCE_GENERAL = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/general", false); //$NON-NLS-1$
- DEBUG_PREFERENCE_SET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/set", false); //$NON-NLS-1$
- DEBUG_PREFERENCE_GET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/get", false); //$NON-NLS-1$
- }
-
- protected class SortedProperties extends Properties {
-
- private static final long serialVersionUID = 1L;
-
- public SortedProperties() {
- super();
- }
-
- /* (non-Javadoc)
- * @see java.util.Hashtable#keys()
- */
- public synchronized Enumeration keys() {
- TreeSet set = new TreeSet();
- for (Enumeration e = super.keys(); e.hasMoreElements();)
- set.add(e.nextElement());
- return Collections.enumeration(set);
- }
-
- /* (non-Javadoc)
- * @see java.util.Hashtable#entrySet()
- */
- public Set entrySet() {
- TreeSet set = new TreeSet(new Comparator() {
- public int compare(Object e1, Object e2) {
- String s1 = (String) ((Map.Entry) e1).getKey();
- String s2 = (String) ((Map.Entry) e2).getKey();
- return s1.compareTo(s2);
- }
- });
- for (Iterator i = super.entrySet().iterator(); i.hasNext();)
- set.add(i.next());
- return set;
- }
- }
-
- public EclipsePreferences() {
- this(null, null);
- }
-
- protected EclipsePreferences(EclipsePreferences parent, String name) {
- super();
- this.parent = parent;
- this.name = name;
- this.cachedPath = null; // make sure the cached path is cleared after setting the parent
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#absolutePath()
- */
- public String absolutePath() {
- if (cachedPath == null) {
- if (parent == null)
- cachedPath = PATH_SEPARATOR;
- else {
- String parentPath = parent.absolutePath();
- // if the parent is the root then we don't have to add a separator
- // between the parent path and our path
- if (parentPath.length() == 1)
- cachedPath = parentPath + name();
- else
- cachedPath = parentPath + PATH_SEPARATOR + name();
- }
- }
- return cachedPath;
- }
-
- public void accept(IPreferenceNodeVisitor visitor) throws BackingStoreException {
- if (!visitor.visit(this))
- return;
- IEclipsePreferences[] toVisit = getChildren(true);
- for (int i = 0; i < toVisit.length; i++)
- toVisit[i].accept(visitor);
- }
-
- protected synchronized IEclipsePreferences addChild(String childName, IEclipsePreferences child) {
- //Thread safety: synchronize method to protect modification of children field
- if (children == null)
- children = Collections.synchronizedMap(new HashMap());
- children.put(childName, child == null ? (Object) childName : child);
- return child;
- }
-
- /*
- * @see org.eclipse.core.runtime.IEclipsePreferences#addNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.INodeChangeListener)
- */
- public void addNodeChangeListener(INodeChangeListener listener) {
- checkRemoved();
- if (nodeChangeListeners == null)
- nodeChangeListeners = new ListenerList();
- nodeChangeListeners.add(listener);
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Added preference node change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /*
- * @see org.eclipse.core.runtime.IEclipsePreferences#addPreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
- */
- public void addPreferenceChangeListener(IPreferenceChangeListener listener) {
- checkRemoved();
- if (preferenceChangeListeners == null)
- preferenceChangeListeners = new ListenerList();
- preferenceChangeListeners.add(listener);
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Added preference property change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- private IEclipsePreferences calculateRoot() {
- IEclipsePreferences result = this;
- while (result.parent() != null)
- result = (IEclipsePreferences) result.parent();
- return result;
- }
-
- /*
- * Convenience method for throwing an exception when methods
- * are called on a removed node.
- */
- protected void checkRemoved() {
- if (removed)
- throw new IllegalStateException(NLS.bind(PrefsMessages.preferences_removedNode, name));
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#childrenNames()
- */
- public String[] childrenNames() {
- // illegal state if this node has been removed
- checkRemoved();
- return internalChildNames();
- }
-
- protected String[] internalChildNames() {
- Map temp = children;
- if (temp == null || temp.size() == 0)
- return EMPTY_STRING_ARRAY;
- return (String[]) temp.keySet().toArray(EMPTY_STRING_ARRAY);
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#clear()
- */
- public void clear() {
- // illegal state if this node has been removed
- checkRemoved();
- // call each one separately (instead of Properties.clear) so
- // clients get change notification
- String[] keys = properties.keys();
- for (int i = 0; i < keys.length; i++)
- remove(keys[i]);
- makeDirty();
- }
-
- protected String[] computeChildren(IPath root) {
- if (root == null)
- return EMPTY_STRING_ARRAY;
- IPath dir = root.append(DEFAULT_PREFERENCES_DIRNAME);
- final ArrayList result = new ArrayList();
- final String extension = '.' + PREFS_FILE_EXTENSION;
- File file = dir.toFile();
- File[] totalFiles = file.listFiles();
- if (totalFiles != null) {
- for (int i = 0; i < totalFiles.length; i++) {
- if (totalFiles[i].isFile()) {
- String filename = totalFiles[i].getName();
- if (filename.endsWith(extension)) {
- String shortName = filename.substring(0, filename.length() - extension.length());
- result.add(shortName);
- }
- }
- }
- }
- return (String[]) result.toArray(EMPTY_STRING_ARRAY);
- }
-
- protected IPath computeLocation(IPath root, String qualifier) {
- return root == null ? null : root.append(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION);
- }
-
- /*
- * Version 1 (current version)
- * path/key=value
- */
- protected static void convertFromProperties(EclipsePreferences node, Properties table, boolean notify) {
- String version = table.getProperty(VERSION_KEY);
- if (version == null || !VERSION_VALUE.equals(version)) {
- // ignore for now
- }
- table.remove(VERSION_KEY);
- for (Iterator i = table.keySet().iterator(); i.hasNext();) {
- String fullKey = (String) i.next();
- String value = table.getProperty(fullKey);
- if (value != null) {
- String[] splitPath = decodePath(fullKey);
- String path = splitPath[0];
- path = makeRelative(path);
- String key = splitPath[1];
- if (DEBUG_PREFERENCE_SET)
- PrefsMessages.message("Setting preference: " + path + '/' + key + '=' + value); //$NON-NLS-1$
- //use internal methods to avoid notifying listeners
- EclipsePreferences childNode = (EclipsePreferences) node.internalNode(path, false, null);
- String oldValue = childNode.internalPut(key, value);
- // notify listeners if applicable
- if (notify && !value.equals(oldValue))
- childNode.firePreferenceEvent(key, oldValue, value);
- }
- }
- PreferencesService.getDefault().shareStrings();
- }
-
- /*
- * Helper method to persist a Properties object to the filesystem. We use this
- * helper so we can remove the date/timestamp that Properties#store always
- * puts in the file.
- */
- protected static void write(Properties properties, IPath location) throws BackingStoreException {
- // create the parent dirs if they don't exist
- File parentFile = location.toFile().getParentFile();
- if (parentFile == null)
- return;
- parentFile.mkdirs();
-
- OutputStream output = null;
- try {
- output = new BufferedOutputStream(new FileOutputStream(new File(location.toOSString())));
- output.write(removeTimestampFromTable(properties).getBytes("UTF-8")); //$NON-NLS-1$
- output.flush();
- } catch (IOException e) {
- String message = NLS.bind(PrefsMessages.preferences_saveException, location);
- log(new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e));
- throw new BackingStoreException(message);
- } finally {
- if (output != null)
- try {
- output.close();
- } catch (IOException e) {
- // ignore
- }
- }
- }
-
- protected static String removeTimestampFromTable(Properties properties) throws IOException {
- // store the properties in a string and then skip the first line (date/timestamp)
- Writer writer = new StringWriter();
- properties.store(writer, null);
- String string = writer.toString();
- String separator = System.getProperty("line.separator"); //$NON-NLS-1$
- return string.substring(string.indexOf(separator) + separator.length());
- }
-
- /*
- * Helper method to convert this node to a Properties file suitable
- * for persistence.
- */
- protected Properties convertToProperties(Properties result, String prefix) throws BackingStoreException {
- // add the key/value pairs from this node
- boolean addSeparator = prefix.length() != 0;
- //thread safety: copy reference in case of concurrent change
- ImmutableMap temp = properties;
- String[] keys = temp.keys();
- for (int i = 0, imax = keys.length; i < imax; i++) {
- String value = temp.get(keys[i]);
- if (value != null)
- result.put(encodePath(prefix, keys[i]), value);
- }
- // recursively add the child information
- IEclipsePreferences[] childNodes = getChildren(true);
- for (int i = 0; i < childNodes.length; i++) {
- EclipsePreferences child = (EclipsePreferences) childNodes[i];
- String fullPath = addSeparator ? prefix + PATH_SEPARATOR + child.name() : child.name();
- child.convertToProperties(result, fullPath);
- }
- PreferencesService.getDefault().shareStrings();
- return result;
- }
-
- /*
- * @see org.eclipse.core.runtime.preferences.IScope#create(org.eclipse.core.runtime.preferences.IEclipsePreferences)
- */
- public IEclipsePreferences create(IEclipsePreferences nodeParent, String nodeName) {
- return create((EclipsePreferences) nodeParent, nodeName, null);
- }
-
- protected boolean isLoading() {
- return loading;
- }
-
- protected void setLoading(boolean isLoading) {
- loading = isLoading;
- }
-
- public IEclipsePreferences create(EclipsePreferences nodeParent, String nodeName, Object context) {
- EclipsePreferences result = internalCreate(nodeParent, nodeName, context);
- nodeParent.addChild(nodeName, result);
- IEclipsePreferences loadLevel = result.getLoadLevel();
-
- // if this node or a parent node is not the load level then return
- if (loadLevel == null)
- return result;
-
- // if the result node is not a load level, then a child must be
- if (result != loadLevel)
- return result;
-
- // the result node is a load level
- if (isAlreadyLoaded(result) || result.isLoading())
- return result;
- try {
- result.setLoading(true);
- result.loadLegacy();
- result.load();
- result.loaded();
- result.flush();
- } catch (BackingStoreException e) {
- IPath location = result.getLocation();
- String message = NLS.bind(PrefsMessages.preferences_loadException, location == null ? EMPTY_STRING : location.toString());
- IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
- RuntimeLog.log(status);
- } finally {
- result.setLoading(false);
- }
- return result;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#flush()
- */
- synchronized public void flush() throws BackingStoreException {
- // illegal state if this node has been removed
- checkRemoved();
-
- IEclipsePreferences loadLevel = getLoadLevel();
-
- // if this node or a parent is not the load level, then flush the children
- if (loadLevel == null) {
- String[] childrenNames = childrenNames();
- for (int i = 0; i < childrenNames.length; i++)
- node(childrenNames[i]).flush();
- return;
- }
-
- // a parent is the load level for this node
- if (this != loadLevel) {
- loadLevel.flush();
- return;
- }
-
- // this node is a load level
- // any work to do?
- if (!dirty)
- return;
- //remove dirty bit before saving, to ensure that concurrent
- //changes during save mark the store as dirty
- dirty = false;
- try {
- save();
- } catch (BackingStoreException e) {
- //mark it dirty again because the save failed
- dirty = true;
- throw e;
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#get(java.lang.String, java.lang.String)
- */
- public String get(String key, String defaultValue) {
- String value = internalGet(key);
- return value == null ? defaultValue : value;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getBoolean(java.lang.String, boolean)
- */
- public boolean getBoolean(String key, boolean defaultValue) {
- String value = internalGet(key);
- return value == null ? defaultValue : TRUE.equalsIgnoreCase(value);
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getByteArray(java.lang.String, byte[])
- */
- public byte[] getByteArray(String key, byte[] defaultValue) {
- String value = internalGet(key);
- return value == null ? defaultValue : Base64.decode(value.getBytes());
- }
-
- /*
- * Return a boolean value indicating whether or not a child with the given
- * name is known to this node.
- */
- protected synchronized boolean childExists(String childName) {
- if (children == null)
- return false;
- return children.get(childName) != null;
- }
-
- /**
- * Thread safe way to obtain a child for a given key. Returns the child
- * that matches the given key, or null if there is no matching child.
- */
- protected IEclipsePreferences getChild(String key, Object context, boolean create) {
- synchronized (this) {
- if (children == null)
- return null;
- Object value = children.get(key);
- if (value == null)
- return null;
- if (value instanceof IEclipsePreferences)
- return (IEclipsePreferences) value;
- // if we aren't supposed to create this node, then
- // just return null
- if (!create)
- return null;
- }
- return addChild(key, create(this, key, context));
- }
-
- /**
- * Thread safe way to obtain all children of this node. Never returns null.
- */
- protected IEclipsePreferences[] getChildren(boolean create) {
- ArrayList result = new ArrayList();
- String[] names = internalChildNames();
- for (int i = 0; i < names.length; i++) {
- IEclipsePreferences child = getChild(names[i], null, create);
- if (child != null)
- result.add(child);
- }
- return (IEclipsePreferences[]) result.toArray(EMPTY_NODE_ARRAY);
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getDouble(java.lang.String, double)
- */
- public double getDouble(String key, double defaultValue) {
- String value = internalGet(key);
- double result = defaultValue;
- if (value != null)
- try {
- result = Double.parseDouble(value);
- } catch (NumberFormatException e) {
- // use default
- }
- return result;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getFloat(java.lang.String, float)
- */
- public float getFloat(String key, float defaultValue) {
- String value = internalGet(key);
- float result = defaultValue;
- if (value != null)
- try {
- result = Float.parseFloat(value);
- } catch (NumberFormatException e) {
- // use default
- }
- return result;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getInt(java.lang.String, int)
- */
- public int getInt(String key, int defaultValue) {
- String value = internalGet(key);
- int result = defaultValue;
- if (value != null)
- try {
- result = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- // use default
- }
- return result;
- }
-
- protected IEclipsePreferences getLoadLevel() {
- return null;
- }
-
- /*
- * Subclasses to over-ride
- */
- protected IPath getLocation() {
- return null;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#getLong(java.lang.String, long)
- */
- public long getLong(String key, long defaultValue) {
- String value = internalGet(key);
- long result = defaultValue;
- if (value != null)
- try {
- result = Long.parseLong(value);
- } catch (NumberFormatException e) {
- // use default
- }
- return result;
- }
-
- protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
- return new EclipsePreferences(nodeParent, nodeName);
- }
-
- /**
- * Returns the existing value at the given key, or null if
- * no such value exists.
- */
- protected String internalGet(String key) {
- // throw NPE if key is null
- if (key == null)
- throw new NullPointerException();
- // illegal state if this node has been removed
- checkRemoved();
- String result = properties.get(key);
- if (DEBUG_PREFERENCE_GET)
- PrefsMessages.message("Getting preference value: " + absolutePath() + '/' + key + "->" + result); //$NON-NLS-1$ //$NON-NLS-2$
- return result;
- }
-
- /**
- * Implements the node(String) method, and optionally notifies listeners.
- */
- protected IEclipsePreferences internalNode(String path, boolean notify, Object context) {
-
- // illegal state if this node has been removed
- checkRemoved();
-
- // short circuit this node
- if (path.length() == 0)
- return this;
-
- // if we have an absolute path use the root relative to
- // this node instead of the global root
- // in case we have a different hierarchy. (e.g. export)
- if (path.charAt(0) == IPath.SEPARATOR)
- return (IEclipsePreferences) calculateRoot().node(path.substring(1));
-
- int index = path.indexOf(IPath.SEPARATOR);
- String key = index == -1 ? path : path.substring(0, index);
- boolean added = false;
- IEclipsePreferences child = getChild(key, context, true);
- if (child == null) {
- child = create(this, key, context);
- added = true;
- }
- // notify listeners if a child was added
- if (added && notify)
- fireNodeEvent(new NodeChangeEvent(this, child), true);
- return (IEclipsePreferences) child.node(index == -1 ? EMPTY_STRING : path.substring(index + 1));
- }
-
- /**
- * Stores the given (key,value) pair, performing lazy initialization of the
- * properties field if necessary. Returns the old value for the given key,
- * or null if no value existed.
- */
- protected String internalPut(String key, String newValue) {
- // illegal state if this node has been removed
- checkRemoved();
- String oldValue = properties.get(key);
- if (oldValue != null && oldValue.equals(newValue))
- return oldValue;
- if (DEBUG_PREFERENCE_SET)
- PrefsMessages.message("Setting preference: " + absolutePath() + '/' + key + '=' + newValue); //$NON-NLS-1$
- properties = properties.put(key, newValue);
- return oldValue;
- }
-
- /*
- * Subclasses to over-ride.
- */
- protected boolean isAlreadyLoaded(IEclipsePreferences node) {
- return true;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#keys()
- */
- public String[] keys() {
- // illegal state if this node has been removed
- checkRemoved();
- return properties.keys();
- }
-
- /**
- * Loads the preference node. This method returns silently if the node does not exist
- * in the backing store (for example non-existent project).
- *
- * @throws BackingStoreException if the node exists in the backing store but it
- * could not be loaded
- */
- protected void load() throws BackingStoreException {
- load(getLocation());
- }
-
- protected static Properties loadProperties(IPath location) throws BackingStoreException {
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Loading preferences from file: " + location); //$NON-NLS-1$
- InputStream input = null;
- Properties result = new Properties();
- try {
- input = new BufferedInputStream(new FileInputStream(location.toFile()));
- result.load(input);
- } catch (FileNotFoundException e) {
- // file doesn't exist but that's ok.
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Preference file does not exist: " + location); //$NON-NLS-1$
- return result;
- } catch (IOException e) {
- String message = NLS.bind(PrefsMessages.preferences_loadException, location);
- log(new Status(IStatus.INFO, PrefsMessages.OWNER_NAME, IStatus.INFO, message, e));
- throw new BackingStoreException(message);
- } finally {
- if (input != null)
- try {
- input.close();
- } catch (IOException e) {
- // ignore
- }
- }
- return result;
- }
-
- protected void load(IPath location) throws BackingStoreException {
- if (location == null) {
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
- return;
- }
- Properties fromDisk = loadProperties(location);
- convertFromProperties(this, fromDisk, false);
- }
-
- protected void loaded() {
- // do nothing
- }
-
- protected void loadLegacy() {
- // sub-classes to over-ride if necessary
- }
-
- public static void log(IStatus status) {
- RuntimeLog.log(status);
- }
-
- protected void makeDirty() {
- EclipsePreferences node = this;
- while (node != null && !node.removed) {
- node.dirty = true;
- node = (EclipsePreferences) node.parent();
- }
- }
-
- public boolean isDirty() {
- return dirty;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#name()
- */
- public String name() {
- return name;
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#node(java.lang.String)
- */
- public Preferences node(String pathName) {
- return internalNode(pathName, true, null);
- }
-
- protected void fireNodeEvent(final NodeChangeEvent event, final boolean added) {
- if (nodeChangeListeners == null)
- return;
- Object[] listeners = nodeChangeListeners.getListeners();
- for (int i = 0; i < listeners.length; i++) {
- final INodeChangeListener listener = (INodeChangeListener) listeners[i];
- ISafeRunnable job = new ISafeRunnable() {
- public void handleException(Throwable exception) {
- // already logged in Platform#run()
- }
-
- public void run() throws Exception {
- if (added)
- listener.added(event);
- else
- listener.removed(event);
- }
- };
- SafeRunner.run(job);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#nodeExists(java.lang.String)
- */
- public boolean nodeExists(String path) throws BackingStoreException {
- // short circuit for checking this node
- if (path.length() == 0)
- return !removed;
-
- // illegal state if this node has been removed.
- // do this AFTER checking for the empty string.
- checkRemoved();
-
- // use the root relative to this node instead of the global root
- // in case we have a different hierarchy. (e.g. export)
- if (path.charAt(0) == IPath.SEPARATOR)
- return calculateRoot().nodeExists(path.substring(1));
-
- int index = path.indexOf(IPath.SEPARATOR);
- boolean noSlash = index == -1;
-
- // if we are looking for a simple child then just look in the table and return
- if (noSlash)
- return childExists(path);
-
- // otherwise load the parent of the child and then recursively ask
- String childName = path.substring(0, index);
- if (!childExists(childName))
- return false;
- IEclipsePreferences child = getChild(childName, null, true);
- if (child == null)
- return false;
- return child.nodeExists(path.substring(index + 1));
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#parent()
- */
- public Preferences parent() {
- // illegal state if this node has been removed
- checkRemoved();
- return parent;
- }
-
- /*
- * Convenience method for notifying preference change listeners.
- */
- protected void firePreferenceEvent(String key, Object oldValue, Object newValue) {
- if (preferenceChangeListeners == null)
- return;
- Object[] listeners = preferenceChangeListeners.getListeners();
- final PreferenceChangeEvent event = new PreferenceChangeEvent(this, key, oldValue, newValue);
- for (int i = 0; i < listeners.length; i++) {
- final IPreferenceChangeListener listener = (IPreferenceChangeListener) listeners[i];
- ISafeRunnable job = new ISafeRunnable() {
- public void handleException(Throwable exception) {
- // already logged in Platform#run()
- }
-
- public void run() throws Exception {
- listener.preferenceChange(event);
- }
- };
- SafeRunner.run(job);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#put(java.lang.String, java.lang.String)
- */
- public void put(String key, String newValue) {
- if (key == null || newValue == null)
- throw new NullPointerException();
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putBoolean(java.lang.String, boolean)
- */
- public void putBoolean(String key, boolean value) {
- if (key == null)
- throw new NullPointerException();
- String newValue = value ? TRUE : FALSE;
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putByteArray(java.lang.String, byte[])
- */
- public void putByteArray(String key, byte[] value) {
- if (key == null || value == null)
- throw new NullPointerException();
- String newValue = new String(Base64.encode(value));
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putDouble(java.lang.String, double)
- */
- public void putDouble(String key, double value) {
- if (key == null)
- throw new NullPointerException();
- String newValue = Double.toString(value);
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putFloat(java.lang.String, float)
- */
- public void putFloat(String key, float value) {
- if (key == null)
- throw new NullPointerException();
- String newValue = Float.toString(value);
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putInt(java.lang.String, int)
- */
- public void putInt(String key, int value) {
- if (key == null)
- throw new NullPointerException();
- String newValue = Integer.toString(value);
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#putLong(java.lang.String, long)
- */
- public void putLong(String key, long value) {
- if (key == null)
- throw new NullPointerException();
- String newValue = Long.toString(value);
- String oldValue = internalPut(key, newValue);
- if (!newValue.equals(oldValue)) {
- makeDirty();
- firePreferenceEvent(key, oldValue, newValue);
- }
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#remove(java.lang.String)
- */
- public void remove(String key) {
- // illegal state if this node has been removed
- checkRemoved();
- String oldValue = properties.get(key);
- if (oldValue == null)
- return;
- properties = properties.removeKey(key);
- makeDirty();
- firePreferenceEvent(key, oldValue, null);
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#removeNode()
- */
- public void removeNode() throws BackingStoreException {
- // illegal state if this node has been removed
- checkRemoved();
- // clear all the property values. do it "the long way" so
- // everyone gets notification
- String[] keys = keys();
- for (int i = 0; i < keys.length; i++)
- remove(keys[i]);
- // don't remove the global root or the scope root from the
- // parent but remove all its children
- if (parent != null && !(parent instanceof RootPreferences)) {
- // remove the node from the parent's collection and notify listeners
- removed = true;
- parent.removeNode(this);
- }
- IEclipsePreferences[] childNodes = getChildren(false);
- for (int i = 0; i < childNodes.length; i++)
- try {
- childNodes[i].removeNode();
- } catch (IllegalStateException e) {
- // ignore since we only get this exception if we have already
- // been removed. no work to do.
- }
- }
-
- /*
- * Remove the child from the collection and notify the listeners if something
- * was actually removed.
- */
- protected void removeNode(IEclipsePreferences child) {
- boolean wasRemoved = false;
- synchronized (this) {
- if (children != null) {
- wasRemoved = children.remove(child.name()) != null;
- if (wasRemoved)
- makeDirty();
- if (children.isEmpty())
- children = null;
- }
- }
- if (wasRemoved)
- fireNodeEvent(new NodeChangeEvent(this, child), false);
- }
-
- /*
- * Remove non-initialized node from the collection.
- */
- protected void removeNode(String key) {
- synchronized (this) {
- if (children != null) {
- boolean wasRemoved = children.remove(key) != null;
- if (wasRemoved)
- makeDirty();
- if (children.isEmpty())
- children = null;
- }
- }
- }
-
- /*
- * @see org.eclipse.core.runtime.IEclipsePreferences#removeNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.removeNodeChangeListener)
- */
- public void removeNodeChangeListener(INodeChangeListener listener) {
- checkRemoved();
- if (nodeChangeListeners == null)
- return;
- nodeChangeListeners.remove(listener);
- if (nodeChangeListeners.size() == 0)
- nodeChangeListeners = null;
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Removed preference node change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /*
- * @see org.eclipse.core.runtime.IEclipsePreferences#removePreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
- */
- public void removePreferenceChangeListener(IPreferenceChangeListener listener) {
- checkRemoved();
- if (preferenceChangeListeners == null)
- return;
- preferenceChangeListeners.remove(listener);
- if (preferenceChangeListeners.size() == 0)
- preferenceChangeListeners = null;
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Removed preference property change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
- }
-
- /**
- * Saves the preference node. This method returns silently if the node does not exist
- * in the backing store (for example non-existent project)
- *
- * @throws BackingStoreException if the node exists in the backing store but it
- * could not be saved
- */
- protected void save() throws BackingStoreException {
- save(getLocation());
- }
-
- protected void save(IPath location) throws BackingStoreException {
- if (location == null) {
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
- return;
- }
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Saving preferences to file: " + location); //$NON-NLS-1$
- Properties table = convertToProperties(new SortedProperties(), EMPTY_STRING);
- if (table.isEmpty()) {
- // nothing to save. delete existing file if one exists.
- if (location.toFile().exists() && !location.toFile().delete()) {
- String message = NLS.bind(PrefsMessages.preferences_failedDelete, location);
- log(new Status(IStatus.WARNING, PrefsMessages.OWNER_NAME, IStatus.WARNING, message, null));
- }
- return;
- }
- table.put(VERSION_KEY, VERSION_VALUE);
- write(table, location);
- }
-
- /**
- * Traverses the preference hierarchy rooted at this node, and adds
- * all preference key and value strings to the provided pool. If an added
- * string was already in the pool, all references will be replaced with the
- * canonical copy of the string.
- *
- * @param pool The pool to share strings in
- */
- public void shareStrings(StringPool pool) {
- properties.shareStrings(pool);
- IEclipsePreferences[] myChildren = getChildren(false);
- for (int i = 0; i < myChildren.length; i++)
- if (myChildren[i] instanceof EclipsePreferences)
- ((EclipsePreferences) myChildren[i]).shareStrings(pool);
- }
-
- /*
- * Encode the given path and key combo to a form which is suitable for
- * persisting or using when searching. If the key contains a slash character
- * then we must use a double-slash to indicate the end of the
- * path/the beginning of the key.
- */
- public static String encodePath(String path, String key) {
- String result;
- int pathLength = path == null ? 0 : path.length();
- if (key.indexOf(IPath.SEPARATOR) == -1) {
- if (pathLength == 0)
- result = key;
- else
- result = path + IPath.SEPARATOR + key;
- } else {
- if (pathLength == 0)
- result = DOUBLE_SLASH + key;
- else
- result = path + DOUBLE_SLASH + key;
- }
- return result;
- }
-
- /*
- * Return the segment from the given path or null.
- * "segment" parameter is 0-based.
- */
- public static String getSegment(String path, int segment) {
- int start = path.indexOf(IPath.SEPARATOR) == 0 ? 1 : 0;
- int end = path.indexOf(IPath.SEPARATOR, start);
- if (end == path.length() - 1)
- end = -1;
- for (int i = 0; i < segment; i++) {
- if (end == -1)
- return null;
- start = end + 1;
- end = path.indexOf(IPath.SEPARATOR, start);
- }
- if (end == -1)
- end = path.length();
- return path.substring(start, end);
- }
-
- public static int getSegmentCount(String path) {
- StringTokenizer tokenizer = new StringTokenizer(path, String.valueOf(IPath.SEPARATOR));
- return tokenizer.countTokens();
- }
-
- /*
- * Return a relative path
- */
- public static String makeRelative(String path) {
- String result = path;
- if (path == null)
- return EMPTY_STRING;
- if (path.length() > 0 && path.charAt(0) == IPath.SEPARATOR)
- result = path.length() == 0 ? EMPTY_STRING : path.substring(1);
- return result;
- }
-
- /*
- * Return a 2 element String array.
- * element 0 - the path
- * element 1 - the key
- * The path may be null.
- * The key is never null.
- */
- public static String[] decodePath(String fullPath) {
- String key = null;
- String path = null;
-
- // check to see if we have an indicator which tells us where the path ends
- int index = fullPath.indexOf(DOUBLE_SLASH);
- if (index == -1) {
- // we don't have a double-slash telling us where the path ends
- // so the path is up to the last slash character
- int lastIndex = fullPath.lastIndexOf(IPath.SEPARATOR);
- if (lastIndex == -1) {
- key = fullPath;
- } else {
- path = fullPath.substring(0, lastIndex);
- key = fullPath.substring(lastIndex + 1);
- }
- } else {
- // the child path is up to the double-slash and the key
- // is the string after it
- path = fullPath.substring(0, index);
- key = fullPath.substring(index + 2);
- }
-
- // adjust if we have an absolute path
- if (path != null)
- if (path.length() == 0)
- path = null;
- else if (path.charAt(0) == IPath.SEPARATOR)
- path = path.substring(1);
-
- return new String[] {path, key};
- }
-
- /*
- * @see org.osgi.service.prefs.Preferences#sync()
- */
-
- public void sync() throws BackingStoreException {
- // illegal state if this node has been removed
- checkRemoved();
- IEclipsePreferences node = getLoadLevel();
- if (node == null) {
- if (DEBUG_PREFERENCE_GENERAL)
- PrefsMessages.message("Preference node is not a load root: " + absolutePath()); //$NON-NLS-1$
- return;
- }
- if (node instanceof EclipsePreferences) {
- ((EclipsePreferences) node).load();
- node.flush();
- }
- }
-
- public String toDeepDebugString() {
- final StringBuffer buffer = new StringBuffer();
- IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
- public boolean visit(IEclipsePreferences node) throws BackingStoreException {
- buffer.append(node);
- buffer.append('\n');
- String[] keys = node.keys();
- for (int i = 0; i < keys.length; i++) {
- buffer.append(node.absolutePath());
- buffer.append(PATH_SEPARATOR);
- buffer.append(keys[i]);
- buffer.append('=');
- buffer.append(node.get(keys[i], "*default*")); //$NON-NLS-1$
- buffer.append('\n');
- }
- return true;
- }
- };
- try {
- accept(visitor);
- } catch (BackingStoreException e) {
- System.out.println("Exception while calling #toDeepDebugString()"); //$NON-NLS-1$
- e.printStackTrace();
- }
- return buffer.toString();
- }
-
- public String toString() {
- return absolutePath();
- }
-}
+/*******************************************************************************
+ * Copyright (c) 2004, 2011 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
+ * Julian Chen - fix for bug #92572, jclRM
+ *******************************************************************************/
+package org.eclipse.core.internal.preferences;
+
+import java.io.*;
+import java.util.*;
+import org.eclipse.core.internal.runtime.RuntimeLog;
+import org.eclipse.core.runtime.*;
+import org.eclipse.core.runtime.preferences.*;
+import org.eclipse.osgi.util.NLS;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+
+/**
+ * Represents a node in the Eclipse preference node hierarchy. This class
+ * is used as a default implementation/super class for those nodes which
+ * belong to scopes which are contributed by the Platform.
+ *
+ * Implementation notes:
+ *
+ * - For thread safety, we always synchronize on the node object when writing
+ * the children or properties fields. Must ensure we don't synchronize when calling
+ * client code such as listeners.
+ *
+ * @since 3.0
+ */
+public class EclipsePreferences implements IEclipsePreferences, IScope {
+
+ public static final String DEFAULT_PREFERENCES_DIRNAME = ".settings"; //$NON-NLS-1$
+ public static final String PREFS_FILE_EXTENSION = "prefs"; //$NON-NLS-1$
+ protected static final IEclipsePreferences[] EMPTY_NODE_ARRAY = new IEclipsePreferences[0];
+ protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+ private static final String FALSE = "false"; //$NON-NLS-1$
+ private static final String TRUE = "true"; //$NON-NLS-1$
+ protected static final String VERSION_KEY = "eclipse.preferences.version"; //$NON-NLS-1$
+ protected static final String VERSION_VALUE = "1"; //$NON-NLS-1$
+ protected static final String PATH_SEPARATOR = String.valueOf(IPath.SEPARATOR);
+ protected static final String DOUBLE_SLASH = "//"; //$NON-NLS-1$
+ protected static final String EMPTY_STRING = ""; //$NON-NLS-1$
+
+ private String cachedPath;
+ protected Map children;
+ protected boolean dirty = false;
+ protected boolean loading = false;
+ protected final String name;
+ // the parent of an EclipsePreference node is always an EclipsePreference node. (or null)
+ protected final EclipsePreferences parent;
+ protected ImmutableMap properties = ImmutableMap.EMPTY;
+ protected boolean removed = false;
+ private ListenerList nodeChangeListeners;
+ private ListenerList preferenceChangeListeners;
+
+ public static boolean DEBUG_PREFERENCE_GENERAL = false;
+ public static boolean DEBUG_PREFERENCE_SET = false;
+ public static boolean DEBUG_PREFERENCE_GET = false;
+
+ protected final static String debugPluginName = "org.eclipse.equinox.preferences"; //$NON-NLS-1$
+
+ static {
+ DEBUG_PREFERENCE_GENERAL = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/general", false); //$NON-NLS-1$
+ DEBUG_PREFERENCE_SET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/set", false); //$NON-NLS-1$
+ DEBUG_PREFERENCE_GET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/get", false); //$NON-NLS-1$
+ }
+
+ protected class SortedProperties extends Properties {
+
+ private static final long serialVersionUID = 1L;
+
+ public SortedProperties() {
+ super();
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Hashtable#keys()
+ */
+ public synchronized Enumeration keys() {
+ TreeSet set = new TreeSet();
+ for (Enumeration e = super.keys(); e.hasMoreElements();)
+ set.add(e.nextElement());
+ return Collections.enumeration(set);
+ }
+
+ /* (non-Javadoc)
+ * @see java.util.Hashtable#entrySet()
+ */
+ public Set entrySet() {
+ TreeSet set = new TreeSet(new Comparator() {
+ public int compare(Object e1, Object e2) {
+ String s1 = (String) ((Map.Entry) e1).getKey();
+ String s2 = (String) ((Map.Entry) e2).getKey();
+ return s1.compareTo(s2);
+ }
+ });
+ for (Iterator i = super.entrySet().iterator(); i.hasNext();)
+ set.add(i.next());
+ return set;
+ }
+ }
+
+ public EclipsePreferences() {
+ this(null, null);
+ }
+
+ protected EclipsePreferences(EclipsePreferences parent, String name) {
+ super();
+ this.parent = parent;
+ this.name = name;
+ this.cachedPath = null; // make sure the cached path is cleared after setting the parent
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#absolutePath()
+ */
+ public String absolutePath() {
+ if (cachedPath == null) {
+ if (parent == null)
+ cachedPath = PATH_SEPARATOR;
+ else {
+ String parentPath = parent.absolutePath();
+ // if the parent is the root then we don't have to add a separator
+ // between the parent path and our path
+ if (parentPath.length() == 1)
+ cachedPath = parentPath + name();
+ else
+ cachedPath = parentPath + PATH_SEPARATOR + name();
+ }
+ }
+ return cachedPath;
+ }
+
+ public void accept(IPreferenceNodeVisitor visitor) throws BackingStoreException {
+ if (!visitor.visit(this))
+ return;
+ IEclipsePreferences[] toVisit = getChildren(true);
+ for (int i = 0; i < toVisit.length; i++)
+ toVisit[i].accept(visitor);
+ }
+
+ protected synchronized IEclipsePreferences addChild(String childName, IEclipsePreferences child) {
+ //Thread safety: synchronize method to protect modification of children field
+ if (children == null)
+ children = Collections.synchronizedMap(new HashMap());
+ children.put(childName, child == null ? (Object) childName : child);
+ return child;
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.IEclipsePreferences#addNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.INodeChangeListener)
+ */
+ public void addNodeChangeListener(INodeChangeListener listener) {
+ checkRemoved();
+ if (nodeChangeListeners == null)
+ nodeChangeListeners = new ListenerList();
+ nodeChangeListeners.add(listener);
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Added preference node change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.IEclipsePreferences#addPreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
+ */
+ public void addPreferenceChangeListener(IPreferenceChangeListener listener) {
+ checkRemoved();
+ if (preferenceChangeListeners == null)
+ preferenceChangeListeners = new ListenerList();
+ preferenceChangeListeners.add(listener);
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Added preference property change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ private IEclipsePreferences calculateRoot() {
+ IEclipsePreferences result = this;
+ while (result.parent() != null)
+ result = (IEclipsePreferences) result.parent();
+ return result;
+ }
+
+ /*
+ * Convenience method for throwing an exception when methods
+ * are called on a removed node.
+ */
+ protected void checkRemoved() {
+ if (removed)
+ throw new IllegalStateException(NLS.bind(PrefsMessages.preferences_removedNode, name));
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#childrenNames()
+ */
+ public String[] childrenNames() {
+ // illegal state if this node has been removed
+ checkRemoved();
+ return internalChildNames();
+ }
+
+ protected String[] internalChildNames() {
+ Map temp = children;
+ if (temp == null || temp.size() == 0)
+ return EMPTY_STRING_ARRAY;
+ return (String[]) temp.keySet().toArray(EMPTY_STRING_ARRAY);
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#clear()
+ */
+ public void clear() {
+ // illegal state if this node has been removed
+ checkRemoved();
+ // call each one separately (instead of Properties.clear) so
+ // clients get change notification
+ String[] keys = properties.keys();
+ for (int i = 0; i < keys.length; i++)
+ remove(keys[i]);
+ makeDirty();
+ }
+
+ protected String[] computeChildren(IPath root) {
+ if (root == null)
+ return EMPTY_STRING_ARRAY;
+ IPath dir = root.append(DEFAULT_PREFERENCES_DIRNAME);
+ final ArrayList result = new ArrayList();
+ final String extension = '.' + PREFS_FILE_EXTENSION;
+ File file = dir.toFile();
+ File[] totalFiles = file.listFiles();
+ if (totalFiles != null) {
+ for (int i = 0; i < totalFiles.length; i++) {
+ if (totalFiles[i].isFile()) {
+ String filename = totalFiles[i].getName();
+ if (filename.endsWith(extension)) {
+ String shortName = filename.substring(0, filename.length() - extension.length());
+ result.add(shortName);
+ }
+ }
+ }
+ }
+ return (String[]) result.toArray(EMPTY_STRING_ARRAY);
+ }
+
+ protected IPath computeLocation(IPath root, String qualifier) {
+ return root == null ? null : root.append(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION);
+ }
+
+ /*
+ * Version 1 (current version)
+ * path/key=value
+ */
+ protected static void convertFromProperties(EclipsePreferences node, Properties table, boolean notify) {
+ String version = table.getProperty(VERSION_KEY);
+ if (version == null || !VERSION_VALUE.equals(version)) {
+ // ignore for now
+ }
+ table.remove(VERSION_KEY);
+ for (Iterator i = table.keySet().iterator(); i.hasNext();) {
+ String fullKey = (String) i.next();
+ String value = table.getProperty(fullKey);
+ if (value != null) {
+ String[] splitPath = decodePath(fullKey);
+ String path = splitPath[0];
+ path = makeRelative(path);
+ String key = splitPath[1];
+ if (DEBUG_PREFERENCE_SET)
+ PrefsMessages.message("Setting preference: " + path + '/' + key + '=' + value); //$NON-NLS-1$
+ //use internal methods to avoid notifying listeners
+ EclipsePreferences childNode = (EclipsePreferences) node.internalNode(path, false, null);
+ String oldValue = childNode.internalPut(key, value);
+ // notify listeners if applicable
+ if (notify && !value.equals(oldValue))
+ childNode.firePreferenceEvent(key, oldValue, value);
+ }
+ }
+ PreferencesService.getDefault().shareStrings();
+ }
+
+ /*
+ * Helper method to persist a Properties object to the filesystem. We use this
+ * helper so we can remove the date/timestamp that Properties#store always
+ * puts in the file.
+ */
+ protected static void write(Properties properties, IPath location) throws BackingStoreException {
+ // create the parent dirs if they don't exist
+ File parentFile = location.toFile().getParentFile();
+ if (parentFile == null)
+ return;
+ parentFile.mkdirs();
+
+ OutputStream output = null;
+ try {
+ output = new BufferedOutputStream(new FileOutputStream(new File(location.toOSString())));
+ output.write(removeTimestampFromTable(properties).getBytes("UTF-8")); //$NON-NLS-1$
+ output.flush();
+ } catch (IOException e) {
+ String message = NLS.bind(PrefsMessages.preferences_saveException, location);
+ log(new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e));
+ throw new BackingStoreException(message);
+ } finally {
+ if (output != null)
+ try {
+ output.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ protected static String removeTimestampFromTable(Properties properties) throws IOException {
+ // store the properties in a string and then skip the first line (date/timestamp)
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
+ try {
+ properties.store(output, null);
+ } finally {
+ output.close();
+ }
+ String string = output.toString("UTF-8"); //$NON-NLS-1$
+ String separator = System.getProperty("line.separator"); //$NON-NLS-1$
+ return string.substring(string.indexOf(separator) + separator.length());
+ }
+
+ /*
+ * Helper method to convert this node to a Properties file suitable
+ * for persistence.
+ */
+ protected Properties convertToProperties(Properties result, String prefix) throws BackingStoreException {
+ // add the key/value pairs from this node
+ boolean addSeparator = prefix.length() != 0;
+ //thread safety: copy reference in case of concurrent change
+ ImmutableMap temp = properties;
+ String[] keys = temp.keys();
+ for (int i = 0, imax = keys.length; i < imax; i++) {
+ String value = temp.get(keys[i]);
+ if (value != null)
+ result.put(encodePath(prefix, keys[i]), value);
+ }
+ // recursively add the child information
+ IEclipsePreferences[] childNodes = getChildren(true);
+ for (int i = 0; i < childNodes.length; i++) {
+ EclipsePreferences child = (EclipsePreferences) childNodes[i];
+ String fullPath = addSeparator ? prefix + PATH_SEPARATOR + child.name() : child.name();
+ child.convertToProperties(result, fullPath);
+ }
+ PreferencesService.getDefault().shareStrings();
+ return result;
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.preferences.IScope#create(org.eclipse.core.runtime.preferences.IEclipsePreferences)
+ */
+ public IEclipsePreferences create(IEclipsePreferences nodeParent, String nodeName) {
+ return create((EclipsePreferences) nodeParent, nodeName, null);
+ }
+
+ protected boolean isLoading() {
+ return loading;
+ }
+
+ protected void setLoading(boolean isLoading) {
+ loading = isLoading;
+ }
+
+ public IEclipsePreferences create(EclipsePreferences nodeParent, String nodeName, Object context) {
+ EclipsePreferences result = internalCreate(nodeParent, nodeName, context);
+ nodeParent.addChild(nodeName, result);
+ IEclipsePreferences loadLevel = result.getLoadLevel();
+
+ // if this node or a parent node is not the load level then return
+ if (loadLevel == null)
+ return result;
+
+ // if the result node is not a load level, then a child must be
+ if (result != loadLevel)
+ return result;
+
+ // the result node is a load level
+ if (isAlreadyLoaded(result) || result.isLoading())
+ return result;
+ try {
+ result.setLoading(true);
+ result.loadLegacy();
+ result.load();
+ result.loaded();
+ result.flush();
+ } catch (BackingStoreException e) {
+ IPath location = result.getLocation();
+ String message = NLS.bind(PrefsMessages.preferences_loadException, location == null ? EMPTY_STRING : location.toString());
+ IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
+ RuntimeLog.log(status);
+ } finally {
+ result.setLoading(false);
+ }
+ return result;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#flush()
+ */
+ synchronized public void flush() throws BackingStoreException {
+ // illegal state if this node has been removed
+ checkRemoved();
+
+ IEclipsePreferences loadLevel = getLoadLevel();
+
+ // if this node or a parent is not the load level, then flush the children
+ if (loadLevel == null) {
+ String[] childrenNames = childrenNames();
+ for (int i = 0; i < childrenNames.length; i++)
+ node(childrenNames[i]).flush();
+ return;
+ }
+
+ // a parent is the load level for this node
+ if (this != loadLevel) {
+ loadLevel.flush();
+ return;
+ }
+
+ // this node is a load level
+ // any work to do?
+ if (!dirty)
+ return;
+ //remove dirty bit before saving, to ensure that concurrent
+ //changes during save mark the store as dirty
+ dirty = false;
+ try {
+ save();
+ } catch (BackingStoreException e) {
+ //mark it dirty again because the save failed
+ dirty = true;
+ throw e;
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#get(java.lang.String, java.lang.String)
+ */
+ public String get(String key, String defaultValue) {
+ String value = internalGet(key);
+ return value == null ? defaultValue : value;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getBoolean(java.lang.String, boolean)
+ */
+ public boolean getBoolean(String key, boolean defaultValue) {
+ String value = internalGet(key);
+ return value == null ? defaultValue : TRUE.equalsIgnoreCase(value);
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getByteArray(java.lang.String, byte[])
+ */
+ public byte[] getByteArray(String key, byte[] defaultValue) {
+ String value = internalGet(key);
+ return value == null ? defaultValue : Base64.decode(value.getBytes());
+ }
+
+ /*
+ * Return a boolean value indicating whether or not a child with the given
+ * name is known to this node.
+ */
+ protected synchronized boolean childExists(String childName) {
+ if (children == null)
+ return false;
+ return children.get(childName) != null;
+ }
+
+ /**
+ * Thread safe way to obtain a child for a given key. Returns the child
+ * that matches the given key, or null if there is no matching child.
+ */
+ protected IEclipsePreferences getChild(String key, Object context, boolean create) {
+ synchronized (this) {
+ if (children == null)
+ return null;
+ Object value = children.get(key);
+ if (value == null)
+ return null;
+ if (value instanceof IEclipsePreferences)
+ return (IEclipsePreferences) value;
+ // if we aren't supposed to create this node, then
+ // just return null
+ if (!create)
+ return null;
+ }
+ return addChild(key, create(this, key, context));
+ }
+
+ /**
+ * Thread safe way to obtain all children of this node. Never returns null.
+ */
+ protected IEclipsePreferences[] getChildren(boolean create) {
+ ArrayList result = new ArrayList();
+ String[] names = internalChildNames();
+ for (int i = 0; i < names.length; i++) {
+ IEclipsePreferences child = getChild(names[i], null, create);
+ if (child != null)
+ result.add(child);
+ }
+ return (IEclipsePreferences[]) result.toArray(EMPTY_NODE_ARRAY);
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getDouble(java.lang.String, double)
+ */
+ public double getDouble(String key, double defaultValue) {
+ String value = internalGet(key);
+ double result = defaultValue;
+ if (value != null)
+ try {
+ result = Double.parseDouble(value);
+ } catch (NumberFormatException e) {
+ // use default
+ }
+ return result;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getFloat(java.lang.String, float)
+ */
+ public float getFloat(String key, float defaultValue) {
+ String value = internalGet(key);
+ float result = defaultValue;
+ if (value != null)
+ try {
+ result = Float.parseFloat(value);
+ } catch (NumberFormatException e) {
+ // use default
+ }
+ return result;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getInt(java.lang.String, int)
+ */
+ public int getInt(String key, int defaultValue) {
+ String value = internalGet(key);
+ int result = defaultValue;
+ if (value != null)
+ try {
+ result = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ // use default
+ }
+ return result;
+ }
+
+ protected IEclipsePreferences getLoadLevel() {
+ return null;
+ }
+
+ /*
+ * Subclasses to over-ride
+ */
+ protected IPath getLocation() {
+ return null;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#getLong(java.lang.String, long)
+ */
+ public long getLong(String key, long defaultValue) {
+ String value = internalGet(key);
+ long result = defaultValue;
+ if (value != null)
+ try {
+ result = Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ // use default
+ }
+ return result;
+ }
+
+ protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String nodeName, Object context) {
+ return new EclipsePreferences(nodeParent, nodeName);
+ }
+
+ /**
+ * Returns the existing value at the given key, or null if
+ * no such value exists.
+ */
+ protected String internalGet(String key) {
+ // throw NPE if key is null
+ if (key == null)
+ throw new NullPointerException();
+ // illegal state if this node has been removed
+ checkRemoved();
+ String result = properties.get(key);
+ if (DEBUG_PREFERENCE_GET)
+ PrefsMessages.message("Getting preference value: " + absolutePath() + '/' + key + "->" + result); //$NON-NLS-1$ //$NON-NLS-2$
+ return result;
+ }
+
+ /**
+ * Implements the node(String) method, and optionally notifies listeners.
+ */
+ protected IEclipsePreferences internalNode(String path, boolean notify, Object context) {
+
+ // illegal state if this node has been removed
+ checkRemoved();
+
+ // short circuit this node
+ if (path.length() == 0)
+ return this;
+
+ // if we have an absolute path use the root relative to
+ // this node instead of the global root
+ // in case we have a different hierarchy. (e.g. export)
+ if (path.charAt(0) == IPath.SEPARATOR)
+ return (IEclipsePreferences) calculateRoot().node(path.substring(1));
+
+ int index = path.indexOf(IPath.SEPARATOR);
+ String key = index == -1 ? path : path.substring(0, index);
+ boolean added = false;
+ IEclipsePreferences child = getChild(key, context, true);
+ if (child == null) {
+ child = create(this, key, context);
+ added = true;
+ }
+ // notify listeners if a child was added
+ if (added && notify)
+ fireNodeEvent(new NodeChangeEvent(this, child), true);
+ return (IEclipsePreferences) child.node(index == -1 ? EMPTY_STRING : path.substring(index + 1));
+ }
+
+ /**
+ * Stores the given (key,value) pair, performing lazy initialization of the
+ * properties field if necessary. Returns the old value for the given key,
+ * or null if no value existed.
+ */
+ protected String internalPut(String key, String newValue) {
+ // illegal state if this node has been removed
+ checkRemoved();
+ String oldValue = properties.get(key);
+ if (oldValue != null && oldValue.equals(newValue))
+ return oldValue;
+ if (DEBUG_PREFERENCE_SET)
+ PrefsMessages.message("Setting preference: " + absolutePath() + '/' + key + '=' + newValue); //$NON-NLS-1$
+ properties = properties.put(key, newValue);
+ return oldValue;
+ }
+
+ /*
+ * Subclasses to over-ride.
+ */
+ protected boolean isAlreadyLoaded(IEclipsePreferences node) {
+ return true;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#keys()
+ */
+ public String[] keys() {
+ // illegal state if this node has been removed
+ checkRemoved();
+ return properties.keys();
+ }
+
+ /**
+ * Loads the preference node. This method returns silently if the node does not exist
+ * in the backing store (for example non-existent project).
+ *
+ * @throws BackingStoreException if the node exists in the backing store but it
+ * could not be loaded
+ */
+ protected void load() throws BackingStoreException {
+ load(getLocation());
+ }
+
+ protected static Properties loadProperties(IPath location) throws BackingStoreException {
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Loading preferences from file: " + location); //$NON-NLS-1$
+ InputStream input = null;
+ Properties result = new Properties();
+ try {
+ input = new BufferedInputStream(new FileInputStream(location.toFile()));
+ result.load(input);
+ } catch (FileNotFoundException e) {
+ // file doesn't exist but that's ok.
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Preference file does not exist: " + location); //$NON-NLS-1$
+ return result;
+ } catch (IOException e) {
+ String message = NLS.bind(PrefsMessages.preferences_loadException, location);
+ log(new Status(IStatus.INFO, PrefsMessages.OWNER_NAME, IStatus.INFO, message, e));
+ throw new BackingStoreException(message);
+ } finally {
+ if (input != null)
+ try {
+ input.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ return result;
+ }
+
+ protected void load(IPath location) throws BackingStoreException {
+ if (location == null) {
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ Properties fromDisk = loadProperties(location);
+ convertFromProperties(this, fromDisk, false);
+ }
+
+ protected void loaded() {
+ // do nothing
+ }
+
+ protected void loadLegacy() {
+ // sub-classes to over-ride if necessary
+ }
+
+ public static void log(IStatus status) {
+ RuntimeLog.log(status);
+ }
+
+ protected void makeDirty() {
+ EclipsePreferences node = this;
+ while (node != null && !node.removed) {
+ node.dirty = true;
+ node = (EclipsePreferences) node.parent();
+ }
+ }
+
+ public boolean isDirty() {
+ return dirty;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#name()
+ */
+ public String name() {
+ return name;
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#node(java.lang.String)
+ */
+ public Preferences node(String pathName) {
+ return internalNode(pathName, true, null);
+ }
+
+ protected void fireNodeEvent(final NodeChangeEvent event, final boolean added) {
+ if (nodeChangeListeners == null)
+ return;
+ Object[] listeners = nodeChangeListeners.getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ final INodeChangeListener listener = (INodeChangeListener) listeners[i];
+ ISafeRunnable job = new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // already logged in Platform#run()
+ }
+
+ public void run() throws Exception {
+ if (added)
+ listener.added(event);
+ else
+ listener.removed(event);
+ }
+ };
+ SafeRunner.run(job);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#nodeExists(java.lang.String)
+ */
+ public boolean nodeExists(String path) throws BackingStoreException {
+ // short circuit for checking this node
+ if (path.length() == 0)
+ return !removed;
+
+ // illegal state if this node has been removed.
+ // do this AFTER checking for the empty string.
+ checkRemoved();
+
+ // use the root relative to this node instead of the global root
+ // in case we have a different hierarchy. (e.g. export)
+ if (path.charAt(0) == IPath.SEPARATOR)
+ return calculateRoot().nodeExists(path.substring(1));
+
+ int index = path.indexOf(IPath.SEPARATOR);
+ boolean noSlash = index == -1;
+
+ // if we are looking for a simple child then just look in the table and return
+ if (noSlash)
+ return childExists(path);
+
+ // otherwise load the parent of the child and then recursively ask
+ String childName = path.substring(0, index);
+ if (!childExists(childName))
+ return false;
+ IEclipsePreferences child = getChild(childName, null, true);
+ if (child == null)
+ return false;
+ return child.nodeExists(path.substring(index + 1));
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#parent()
+ */
+ public Preferences parent() {
+ // illegal state if this node has been removed
+ checkRemoved();
+ return parent;
+ }
+
+ /*
+ * Convenience method for notifying preference change listeners.
+ */
+ protected void firePreferenceEvent(String key, Object oldValue, Object newValue) {
+ if (preferenceChangeListeners == null)
+ return;
+ Object[] listeners = preferenceChangeListeners.getListeners();
+ final PreferenceChangeEvent event = new PreferenceChangeEvent(this, key, oldValue, newValue);
+ for (int i = 0; i < listeners.length; i++) {
+ final IPreferenceChangeListener listener = (IPreferenceChangeListener) listeners[i];
+ ISafeRunnable job = new ISafeRunnable() {
+ public void handleException(Throwable exception) {
+ // already logged in Platform#run()
+ }
+
+ public void run() throws Exception {
+ listener.preferenceChange(event);
+ }
+ };
+ SafeRunner.run(job);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#put(java.lang.String, java.lang.String)
+ */
+ public void put(String key, String newValue) {
+ if (key == null || newValue == null)
+ throw new NullPointerException();
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putBoolean(java.lang.String, boolean)
+ */
+ public void putBoolean(String key, boolean value) {
+ if (key == null)
+ throw new NullPointerException();
+ String newValue = value ? TRUE : FALSE;
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putByteArray(java.lang.String, byte[])
+ */
+ public void putByteArray(String key, byte[] value) {
+ if (key == null || value == null)
+ throw new NullPointerException();
+ String newValue = new String(Base64.encode(value));
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putDouble(java.lang.String, double)
+ */
+ public void putDouble(String key, double value) {
+ if (key == null)
+ throw new NullPointerException();
+ String newValue = Double.toString(value);
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putFloat(java.lang.String, float)
+ */
+ public void putFloat(String key, float value) {
+ if (key == null)
+ throw new NullPointerException();
+ String newValue = Float.toString(value);
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putInt(java.lang.String, int)
+ */
+ public void putInt(String key, int value) {
+ if (key == null)
+ throw new NullPointerException();
+ String newValue = Integer.toString(value);
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#putLong(java.lang.String, long)
+ */
+ public void putLong(String key, long value) {
+ if (key == null)
+ throw new NullPointerException();
+ String newValue = Long.toString(value);
+ String oldValue = internalPut(key, newValue);
+ if (!newValue.equals(oldValue)) {
+ makeDirty();
+ firePreferenceEvent(key, oldValue, newValue);
+ }
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#remove(java.lang.String)
+ */
+ public void remove(String key) {
+ // illegal state if this node has been removed
+ checkRemoved();
+ String oldValue = properties.get(key);
+ if (oldValue == null)
+ return;
+ properties = properties.removeKey(key);
+ makeDirty();
+ firePreferenceEvent(key, oldValue, null);
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#removeNode()
+ */
+ public void removeNode() throws BackingStoreException {
+ // illegal state if this node has been removed
+ checkRemoved();
+ // clear all the property values. do it "the long way" so
+ // everyone gets notification
+ String[] keys = keys();
+ for (int i = 0; i < keys.length; i++)
+ remove(keys[i]);
+ // don't remove the global root or the scope root from the
+ // parent but remove all its children
+ if (parent != null && !(parent instanceof RootPreferences)) {
+ // remove the node from the parent's collection and notify listeners
+ removed = true;
+ parent.removeNode(this);
+ }
+ IEclipsePreferences[] childNodes = getChildren(false);
+ for (int i = 0; i < childNodes.length; i++)
+ try {
+ childNodes[i].removeNode();
+ } catch (IllegalStateException e) {
+ // ignore since we only get this exception if we have already
+ // been removed. no work to do.
+ }
+ }
+
+ /*
+ * Remove the child from the collection and notify the listeners if something
+ * was actually removed.
+ */
+ protected void removeNode(IEclipsePreferences child) {
+ boolean wasRemoved = false;
+ synchronized (this) {
+ if (children != null) {
+ wasRemoved = children.remove(child.name()) != null;
+ if (wasRemoved)
+ makeDirty();
+ if (children.isEmpty())
+ children = null;
+ }
+ }
+ if (wasRemoved)
+ fireNodeEvent(new NodeChangeEvent(this, child), false);
+ }
+
+ /*
+ * Remove non-initialized node from the collection.
+ */
+ protected void removeNode(String key) {
+ synchronized (this) {
+ if (children != null) {
+ boolean wasRemoved = children.remove(key) != null;
+ if (wasRemoved)
+ makeDirty();
+ if (children.isEmpty())
+ children = null;
+ }
+ }
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.IEclipsePreferences#removeNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.removeNodeChangeListener)
+ */
+ public void removeNodeChangeListener(INodeChangeListener listener) {
+ checkRemoved();
+ if (nodeChangeListeners == null)
+ return;
+ nodeChangeListeners.remove(listener);
+ if (nodeChangeListeners.size() == 0)
+ nodeChangeListeners = null;
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Removed preference node change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /*
+ * @see org.eclipse.core.runtime.IEclipsePreferences#removePreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
+ */
+ public void removePreferenceChangeListener(IPreferenceChangeListener listener) {
+ checkRemoved();
+ if (preferenceChangeListeners == null)
+ return;
+ preferenceChangeListeners.remove(listener);
+ if (preferenceChangeListeners.size() == 0)
+ preferenceChangeListeners = null;
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Removed preference property change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Saves the preference node. This method returns silently if the node does not exist
+ * in the backing store (for example non-existent project)
+ *
+ * @throws BackingStoreException if the node exists in the backing store but it
+ * could not be saved
+ */
+ protected void save() throws BackingStoreException {
+ save(getLocation());
+ }
+
+ protected void save(IPath location) throws BackingStoreException {
+ if (location == null) {
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Saving preferences to file: " + location); //$NON-NLS-1$
+ Properties table = convertToProperties(new SortedProperties(), EMPTY_STRING);
+ if (table.isEmpty()) {
+ // nothing to save. delete existing file if one exists.
+ if (location.toFile().exists() && !location.toFile().delete()) {
+ String message = NLS.bind(PrefsMessages.preferences_failedDelete, location);
+ log(new Status(IStatus.WARNING, PrefsMessages.OWNER_NAME, IStatus.WARNING, message, null));
+ }
+ return;
+ }
+ table.put(VERSION_KEY, VERSION_VALUE);
+ write(table, location);
+ }
+
+ /**
+ * Traverses the preference hierarchy rooted at this node, and adds
+ * all preference key and value strings to the provided pool. If an added
+ * string was already in the pool, all references will be replaced with the
+ * canonical copy of the string.
+ *
+ * @param pool The pool to share strings in
+ */
+ public void shareStrings(StringPool pool) {
+ properties.shareStrings(pool);
+ IEclipsePreferences[] myChildren = getChildren(false);
+ for (int i = 0; i < myChildren.length; i++)
+ if (myChildren[i] instanceof EclipsePreferences)
+ ((EclipsePreferences) myChildren[i]).shareStrings(pool);
+ }
+
+ /*
+ * Encode the given path and key combo to a form which is suitable for
+ * persisting or using when searching. If the key contains a slash character
+ * then we must use a double-slash to indicate the end of the
+ * path/the beginning of the key.
+ */
+ public static String encodePath(String path, String key) {
+ String result;
+ int pathLength = path == null ? 0 : path.length();
+ if (key.indexOf(IPath.SEPARATOR) == -1) {
+ if (pathLength == 0)
+ result = key;
+ else
+ result = path + IPath.SEPARATOR + key;
+ } else {
+ if (pathLength == 0)
+ result = DOUBLE_SLASH + key;
+ else
+ result = path + DOUBLE_SLASH + key;
+ }
+ return result;
+ }
+
+ /*
+ * Return the segment from the given path or null.
+ * "segment" parameter is 0-based.
+ */
+ public static String getSegment(String path, int segment) {
+ int start = path.indexOf(IPath.SEPARATOR) == 0 ? 1 : 0;
+ int end = path.indexOf(IPath.SEPARATOR, start);
+ if (end == path.length() - 1)
+ end = -1;
+ for (int i = 0; i < segment; i++) {
+ if (end == -1)
+ return null;
+ start = end + 1;
+ end = path.indexOf(IPath.SEPARATOR, start);
+ }
+ if (end == -1)
+ end = path.length();
+ return path.substring(start, end);
+ }
+
+ public static int getSegmentCount(String path) {
+ StringTokenizer tokenizer = new StringTokenizer(path, String.valueOf(IPath.SEPARATOR));
+ return tokenizer.countTokens();
+ }
+
+ /*
+ * Return a relative path
+ */
+ public static String makeRelative(String path) {
+ String result = path;
+ if (path == null)
+ return EMPTY_STRING;
+ if (path.length() > 0 && path.charAt(0) == IPath.SEPARATOR)
+ result = path.length() == 0 ? EMPTY_STRING : path.substring(1);
+ return result;
+ }
+
+ /*
+ * Return a 2 element String array.
+ * element 0 - the path
+ * element 1 - the key
+ * The path may be null.
+ * The key is never null.
+ */
+ public static String[] decodePath(String fullPath) {
+ String key = null;
+ String path = null;
+
+ // check to see if we have an indicator which tells us where the path ends
+ int index = fullPath.indexOf(DOUBLE_SLASH);
+ if (index == -1) {
+ // we don't have a double-slash telling us where the path ends
+ // so the path is up to the last slash character
+ int lastIndex = fullPath.lastIndexOf(IPath.SEPARATOR);
+ if (lastIndex == -1) {
+ key = fullPath;
+ } else {
+ path = fullPath.substring(0, lastIndex);
+ key = fullPath.substring(lastIndex + 1);
+ }
+ } else {
+ // the child path is up to the double-slash and the key
+ // is the string after it
+ path = fullPath.substring(0, index);
+ key = fullPath.substring(index + 2);
+ }
+
+ // adjust if we have an absolute path
+ if (path != null)
+ if (path.length() == 0)
+ path = null;
+ else if (path.charAt(0) == IPath.SEPARATOR)
+ path = path.substring(1);
+
+ return new String[] {path, key};
+ }
+
+ /*
+ * @see org.osgi.service.prefs.Preferences#sync()
+ */
+
+ public void sync() throws BackingStoreException {
+ // illegal state if this node has been removed
+ checkRemoved();
+ IEclipsePreferences node = getLoadLevel();
+ if (node == null) {
+ if (DEBUG_PREFERENCE_GENERAL)
+ PrefsMessages.message("Preference node is not a load root: " + absolutePath()); //$NON-NLS-1$
+ return;
+ }
+ if (node instanceof EclipsePreferences) {
+ ((EclipsePreferences) node).load();
+ node.flush();
+ }
+ }
+
+ public String toDeepDebugString() {
+ final StringBuffer buffer = new StringBuffer();
+ IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
+ public boolean visit(IEclipsePreferences node) throws BackingStoreException {
+ buffer.append(node);
+ buffer.append('\n');
+ String[] keys = node.keys();
+ for (int i = 0; i < keys.length; i++) {
+ buffer.append(node.absolutePath());
+ buffer.append(PATH_SEPARATOR);
+ buffer.append(keys[i]);
+ buffer.append('=');
+ buffer.append(node.get(keys[i], "*default*")); //$NON-NLS-1$
+ buffer.append('\n');
+ }
+ return true;
+ }
+ };
+ try {
+ accept(visitor);
+ } catch (BackingStoreException e) {
+ System.out.println("Exception while calling #toDeepDebugString()"); //$NON-NLS-1$
+ e.printStackTrace();
+ }
+ return buffer.toString();
+ }
+
+ public String toString() {
+ return absolutePath();
+ }
+}

Back to the top