KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > internal > preferences > EclipsePreferences


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Julian Chen - fix for bug #92572, jclRM
11  *******************************************************************************/

12 package org.eclipse.core.internal.preferences;
13
14 import java.io.*;
15 import java.util.*;
16 import org.eclipse.core.internal.runtime.RuntimeLog;
17 import org.eclipse.core.runtime.*;
18 import org.eclipse.core.runtime.preferences.*;
19 import org.eclipse.osgi.util.NLS;
20 import org.osgi.service.prefs.BackingStoreException;
21 import org.osgi.service.prefs.Preferences;
22
23 /**
24  * Represents a node in the Eclipse preference node hierarchy. This class
25  * is used as a default implementation/super class for those nodes which
26  * belong to scopes which are contributed by the Platform.
27  *
28  * Implementation notes:
29  *
30  * - For thread safety, we always synchronize on the node object when writing
31  * the children or properties fields. Must ensure we don't synchronize when calling
32  * client code such as listeners.
33  *
34  * @since 3.0
35  */

36 public class EclipsePreferences implements IEclipsePreferences, IScope {
37
38     public static final String JavaDoc DEFAULT_PREFERENCES_DIRNAME = ".settings"; //$NON-NLS-1$
39
public static final String JavaDoc PREFS_FILE_EXTENSION = "prefs"; //$NON-NLS-1$
40
protected static final IEclipsePreferences[] EMPTY_NODE_ARRAY = new IEclipsePreferences[0];
41     protected static final String JavaDoc[] EMPTY_STRING_ARRAY = new String JavaDoc[0];
42     private static final String JavaDoc FALSE = "false"; //$NON-NLS-1$
43
private static final String JavaDoc TRUE = "true"; //$NON-NLS-1$
44
protected static final String JavaDoc VERSION_KEY = "eclipse.preferences.version"; //$NON-NLS-1$
45
protected static final String JavaDoc VERSION_VALUE = "1"; //$NON-NLS-1$
46
protected static final String JavaDoc PATH_SEPARATOR = String.valueOf(IPath.SEPARATOR);
47     protected static final String JavaDoc DOUBLE_SLASH = "//"; //$NON-NLS-1$
48
protected static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
49

50     private String JavaDoc cachedPath;
51     protected Map children;
52     protected boolean dirty = false;
53     protected boolean loading = false;
54     protected final String JavaDoc name;
55     // the parent of an EclipsePreference node is always an EclipsePreference node. (or null)
56
protected final EclipsePreferences parent;
57     protected ImmutableMap properties = ImmutableMap.EMPTY;
58     protected boolean removed = false;
59     private ListenerList nodeChangeListeners;
60     private ListenerList preferenceChangeListeners;
61
62     public static boolean DEBUG_PREFERENCE_GENERAL = false;
63     public static boolean DEBUG_PREFERENCE_SET = false;
64     public static boolean DEBUG_PREFERENCE_GET = false;
65
66     protected final static String JavaDoc debugPluginName = "org.eclipse.equinox.preferences"; //$NON-NLS-1$
67

68     static {
69         DEBUG_PREFERENCE_GENERAL = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/general", false); //$NON-NLS-1$
70
DEBUG_PREFERENCE_SET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/set", false); //$NON-NLS-1$
71
DEBUG_PREFERENCE_GET = PreferencesOSGiUtils.getDefault().getBooleanDebugOption(debugPluginName + "/get", false); //$NON-NLS-1$
72
}
73
74     public EclipsePreferences() {
75         this(null, null);
76     }
77
78     protected EclipsePreferences(EclipsePreferences parent, String JavaDoc name) {
79         super();
80         this.parent = parent;
81         this.name = name;
82     }
83
84     /*
85      * @see org.osgi.service.prefs.Preferences#absolutePath()
86      */

87     public String JavaDoc absolutePath() {
88         if (cachedPath == null) {
89             if (parent == null)
90                 cachedPath = PATH_SEPARATOR;
91             else {
92                 String JavaDoc parentPath = parent.absolutePath();
93                 // if the parent is the root then we don't have to add a separator
94
// between the parent path and our path
95
if (parentPath.length() == 1)
96                     cachedPath = parentPath + name();
97                 else
98                     cachedPath = parentPath + PATH_SEPARATOR + name();
99             }
100         }
101         return cachedPath;
102     }
103
104     public void accept(IPreferenceNodeVisitor visitor) throws BackingStoreException {
105         if (!visitor.visit(this))
106             return;
107         IEclipsePreferences[] toVisit = getChildren(true);
108         for (int i = 0; i < toVisit.length; i++)
109             toVisit[i].accept(visitor);
110     }
111
112     protected synchronized IEclipsePreferences addChild(String JavaDoc childName, IEclipsePreferences child) {
113         //Thread safety: synchronize method to protect modification of children field
114
if (children == null)
115             children = Collections.synchronizedMap(new HashMap());
116         children.put(childName, child == null ? (Object JavaDoc) childName : child);
117         return child;
118     }
119
120     /*
121      * @see org.eclipse.core.runtime.IEclipsePreferences#addNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.INodeChangeListener)
122      */

123     public void addNodeChangeListener(INodeChangeListener listener) {
124         checkRemoved();
125         if (nodeChangeListeners == null)
126             nodeChangeListeners = new ListenerList();
127         nodeChangeListeners.add(listener);
128         if (DEBUG_PREFERENCE_GENERAL)
129             PrefsMessages.message("Added preference node change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
130
}
131
132     /*
133      * @see org.eclipse.core.runtime.IEclipsePreferences#addPreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
134      */

135     public void addPreferenceChangeListener(IPreferenceChangeListener listener) {
136         checkRemoved();
137         if (preferenceChangeListeners == null)
138             preferenceChangeListeners = new ListenerList();
139         preferenceChangeListeners.add(listener);
140         if (DEBUG_PREFERENCE_GENERAL)
141             PrefsMessages.message("Added preference property change listener: " + listener + " to: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
142
}
143
144     private IEclipsePreferences calculateRoot() {
145         IEclipsePreferences result = this;
146         while (result.parent() != null)
147             result = (IEclipsePreferences) result.parent();
148         return result;
149     }
150
151     /*
152      * Convenience method for throwing an exception when methods
153      * are called on a removed node.
154      */

155     protected void checkRemoved() {
156         if (removed)
157             throw new IllegalStateException JavaDoc(NLS.bind(PrefsMessages.preferences_removedNode, name));
158     }
159
160     /*
161      * @see org.osgi.service.prefs.Preferences#childrenNames()
162      */

163     public String JavaDoc[] childrenNames() {
164         // illegal state if this node has been removed
165
checkRemoved();
166         return internalChildNames();
167     }
168
169     protected String JavaDoc[] internalChildNames() {
170         Map temp = children;
171         if (temp == null || temp.size() == 0)
172             return EMPTY_STRING_ARRAY;
173         return (String JavaDoc[]) temp.keySet().toArray(EMPTY_STRING_ARRAY);
174     }
175
176     /*
177      * @see org.osgi.service.prefs.Preferences#clear()
178      */

179     public void clear() {
180         // illegal state if this node has been removed
181
checkRemoved();
182         // call each one separately (instead of Properties.clear) so
183
// clients get change notification
184
String JavaDoc[] keys = properties.keys();
185         for (int i = 0; i < keys.length; i++)
186             remove(keys[i]);
187         makeDirty();
188     }
189
190     protected String JavaDoc[] computeChildren(IPath root) {
191         if (root == null)
192             return EMPTY_STRING_ARRAY;
193         IPath dir = root.append(DEFAULT_PREFERENCES_DIRNAME);
194         final ArrayList result = new ArrayList();
195         final String JavaDoc extension = '.' + PREFS_FILE_EXTENSION;
196         File file = dir.toFile();
197         File[] totalFiles = file.listFiles();
198         if (totalFiles != null) {
199             for (int i = 0; i < totalFiles.length; i++) {
200                 if (totalFiles[i].isFile()) {
201                     String JavaDoc filename = totalFiles[i].getName();
202                     if (filename.endsWith(extension)) {
203                         String JavaDoc shortName = filename.substring(0, filename.length() - extension.length());
204                         result.add(shortName);
205                     }
206                 }
207             }
208         }
209         return (String JavaDoc[]) result.toArray(EMPTY_STRING_ARRAY);
210     }
211
212     protected IPath computeLocation(IPath root, String JavaDoc qualifier) {
213         return root == null ? null : root.append(DEFAULT_PREFERENCES_DIRNAME).append(qualifier).addFileExtension(PREFS_FILE_EXTENSION);
214     }
215
216     /*
217      * Version 1 (current version)
218      * path/key=value
219      */

220     protected static void convertFromProperties(EclipsePreferences node, Properties table, boolean notify) {
221         String JavaDoc version = table.getProperty(VERSION_KEY);
222         if (version == null || !VERSION_VALUE.equals(version)) {
223             // ignore for now
224
}
225         table.remove(VERSION_KEY);
226         for (Iterator i = table.keySet().iterator(); i.hasNext();) {
227             String JavaDoc fullKey = (String JavaDoc) i.next();
228             String JavaDoc value = table.getProperty(fullKey);
229             if (value != null) {
230                 String JavaDoc[] splitPath = decodePath(fullKey);
231                 String JavaDoc path = splitPath[0];
232                 path = makeRelative(path);
233                 String JavaDoc key = splitPath[1];
234                 if (DEBUG_PREFERENCE_SET)
235                     PrefsMessages.message("Setting preference: " + path + '/' + key + '=' + value); //$NON-NLS-1$
236
//use internal methods to avoid notifying listeners
237
EclipsePreferences childNode = (EclipsePreferences) node.internalNode(path, false, null);
238                 String JavaDoc oldValue = childNode.internalPut(key, value);
239                 // notify listeners if applicable
240
if (notify && !value.equals(oldValue))
241                     node.firePreferenceEvent(key, oldValue, value);
242             }
243         }
244         PreferencesService.getDefault().shareStrings();
245     }
246
247     /*
248      * Helper method to convert this node to a Properties file suitable
249      * for persistence.
250      */

251     protected Properties convertToProperties(Properties result, String JavaDoc prefix) throws BackingStoreException {
252         // add the key/value pairs from this node
253
boolean addSeparator = prefix.length() != 0;
254         //thread safety: copy reference in case of concurrent change
255
ImmutableMap temp = properties;
256         String JavaDoc[] keys = temp.keys();
257         for (int i = 0, imax = keys.length; i < imax; i++) {
258             String JavaDoc value = temp.get(keys[i]);
259             if (value != null)
260                 result.put(encodePath(prefix, keys[i]), value);
261         }
262         // recursively add the child information
263
IEclipsePreferences[] childNodes = getChildren(true);
264         for (int i = 0; i < childNodes.length; i++) {
265             EclipsePreferences child = (EclipsePreferences) childNodes[i];
266             String JavaDoc fullPath = addSeparator ? prefix + PATH_SEPARATOR + child.name() : child.name();
267             child.convertToProperties(result, fullPath);
268         }
269         PreferencesService.getDefault().shareStrings();
270         return result;
271     }
272
273     /*
274      * @see org.eclipse.core.runtime.preferences.IScope#create(org.eclipse.core.runtime.preferences.IEclipsePreferences)
275      */

276     public IEclipsePreferences create(IEclipsePreferences nodeParent, String JavaDoc nodeName) {
277         return create((EclipsePreferences) nodeParent, nodeName, null);
278     }
279
280     protected boolean isLoading() {
281         return loading;
282     }
283
284     protected void setLoading(boolean isLoading) {
285         loading = isLoading;
286     }
287
288     public IEclipsePreferences create(EclipsePreferences nodeParent, String JavaDoc nodeName, Object JavaDoc context) {
289         EclipsePreferences result = internalCreate(nodeParent, nodeName, context);
290         nodeParent.addChild(nodeName, result);
291         IEclipsePreferences loadLevel = result.getLoadLevel();
292
293         // if this node or a parent node is not the load level then return
294
if (loadLevel == null)
295             return result;
296
297         // if the result node is not a load level, then a child must be
298
if (result != loadLevel)
299             return result;
300
301         // the result node is a load level
302
if (isAlreadyLoaded(result) || result.isLoading())
303             return result;
304         try {
305             result.setLoading(true);
306             result.loadLegacy();
307             result.load();
308             result.loaded();
309             result.flush();
310         } catch (BackingStoreException e) {
311             IPath location = result.getLocation();
312             String JavaDoc message = NLS.bind(PrefsMessages.preferences_loadException, location == null ? EMPTY_STRING : location.toString());
313             IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
314             RuntimeLog.log(status);
315         } finally {
316             result.setLoading(false);
317         }
318         return result;
319     }
320
321     /*
322      * @see org.osgi.service.prefs.Preferences#flush()
323      */

324     public void flush() throws BackingStoreException {
325         // illegal state if this node has been removed
326
checkRemoved();
327
328         IEclipsePreferences loadLevel = getLoadLevel();
329
330         // if this node or a parent is not the load level, then flush the children
331
if (loadLevel == null) {
332             String JavaDoc[] childrenNames = childrenNames();
333             for (int i = 0; i < childrenNames.length; i++)
334                 node(childrenNames[i]).flush();
335             return;
336         }
337
338         // a parent is the load level for this node
339
if (this != loadLevel) {
340             loadLevel.flush();
341             return;
342         }
343
344         // this node is a load level
345
// any work to do?
346
if (!dirty)
347             return;
348         //remove dirty bit before saving, to ensure that concurrent
349
//changes during save mark the store as dirty
350
dirty = false;
351         try {
352             save();
353         } catch (BackingStoreException e) {
354             //mark it dirty again because the save failed
355
dirty = true;
356             throw e;
357         }
358     }
359
360     /*
361      * @see org.osgi.service.prefs.Preferences#get(java.lang.String, java.lang.String)
362      */

363     public String JavaDoc get(String JavaDoc key, String JavaDoc defaultValue) {
364         String JavaDoc value = internalGet(key);
365         return value == null ? defaultValue : value;
366     }
367
368     /*
369      * @see org.osgi.service.prefs.Preferences#getBoolean(java.lang.String, boolean)
370      */

371     public boolean getBoolean(String JavaDoc key, boolean defaultValue) {
372         String JavaDoc value = internalGet(key);
373         return value == null ? defaultValue : TRUE.equalsIgnoreCase(value);
374     }
375
376     /*
377      * @see org.osgi.service.prefs.Preferences#getByteArray(java.lang.String, byte[])
378      */

379     public byte[] getByteArray(String JavaDoc key, byte[] defaultValue) {
380         String JavaDoc value = internalGet(key);
381         return value == null ? defaultValue : Base64.decode(value.getBytes());
382     }
383
384     /*
385      * Return a boolean value indicating whether or not a child with the given
386      * name is known to this node.
387      */

388     protected synchronized boolean childExists(String JavaDoc childName) {
389         if (children == null)
390             return false;
391         return children.get(childName) != null;
392     }
393
394     /**
395      * Thread safe way to obtain a child for a given key. Returns the child
396      * that matches the given key, or null if there is no matching child.
397      */

398     protected IEclipsePreferences getChild(String JavaDoc key, Object JavaDoc context, boolean create) {
399         synchronized (this) {
400             if (children == null)
401                 return null;
402             Object JavaDoc value = children.get(key);
403             if (value == null)
404                 return null;
405             if (value instanceof IEclipsePreferences)
406                 return (IEclipsePreferences) value;
407             // if we aren't supposed to create this node, then
408
// just return null
409
if (!create)
410                 return null;
411         }
412         return addChild(key, create(this, key, context));
413     }
414
415     /**
416      * Thread safe way to obtain all children of this node. Never returns null.
417      */

418     protected IEclipsePreferences[] getChildren(boolean create) {
419         ArrayList result = new ArrayList();
420         String JavaDoc[] names = internalChildNames();
421         for (int i = 0; i < names.length; i++) {
422             IEclipsePreferences child = getChild(names[i], null, create);
423             if (child != null)
424                 result.add(child);
425         }
426         return (IEclipsePreferences[]) result.toArray(EMPTY_NODE_ARRAY);
427     }
428
429     /*
430      * @see org.osgi.service.prefs.Preferences#getDouble(java.lang.String, double)
431      */

432     public double getDouble(String JavaDoc key, double defaultValue) {
433         String JavaDoc value = internalGet(key);
434         double result = defaultValue;
435         if (value != null)
436             try {
437                 result = Double.parseDouble(value);
438             } catch (NumberFormatException JavaDoc e) {
439                 // use default
440
}
441         return result;
442     }
443
444     /*
445      * @see org.osgi.service.prefs.Preferences#getFloat(java.lang.String, float)
446      */

447     public float getFloat(String JavaDoc key, float defaultValue) {
448         String JavaDoc value = internalGet(key);
449         float result = defaultValue;
450         if (value != null)
451             try {
452                 result = Float.parseFloat(value);
453             } catch (NumberFormatException JavaDoc e) {
454                 // use default
455
}
456         return result;
457     }
458
459     /*
460      * @see org.osgi.service.prefs.Preferences#getInt(java.lang.String, int)
461      */

462     public int getInt(String JavaDoc key, int defaultValue) {
463         String JavaDoc value = internalGet(key);
464         int result = defaultValue;
465         if (value != null)
466             try {
467                 result = Integer.parseInt(value);
468             } catch (NumberFormatException JavaDoc e) {
469                 // use default
470
}
471         return result;
472     }
473
474     protected IEclipsePreferences getLoadLevel() {
475         return null;
476     }
477
478     /*
479      * Subclasses to over-ride
480      */

481     protected IPath getLocation() {
482         return null;
483     }
484
485     /*
486      * @see org.osgi.service.prefs.Preferences#getLong(java.lang.String, long)
487      */

488     public long getLong(String JavaDoc key, long defaultValue) {
489         String JavaDoc value = internalGet(key);
490         long result = defaultValue;
491         if (value != null)
492             try {
493                 result = Long.parseLong(value);
494             } catch (NumberFormatException JavaDoc e) {
495                 // use default
496
}
497         return result;
498     }
499
500     protected EclipsePreferences internalCreate(EclipsePreferences nodeParent, String JavaDoc nodeName, Object JavaDoc context) {
501         return new EclipsePreferences(nodeParent, nodeName);
502     }
503
504     /**
505      * Returns the existing value at the given key, or null if
506      * no such value exists.
507      */

508     protected String JavaDoc internalGet(String JavaDoc key) {
509         // throw NPE if key is null
510
if (key == null)
511             throw new NullPointerException JavaDoc();
512         // illegal state if this node has been removed
513
checkRemoved();
514         String JavaDoc result = properties.get(key);
515         if (DEBUG_PREFERENCE_GET)
516             PrefsMessages.message("Getting preference value: " + absolutePath() + '/' + key + "->" + result); //$NON-NLS-1$ //$NON-NLS-2$
517
return result;
518     }
519
520     /**
521      * Implements the node(String) method, and optionally notifies listeners.
522      */

523     protected IEclipsePreferences internalNode(String JavaDoc path, boolean notify, Object JavaDoc context) {
524
525         // illegal state if this node has been removed
526
checkRemoved();
527
528         // short circuit this node
529
if (path.length() == 0)
530             return this;
531
532         // if we have an absolute path use the root relative to
533
// this node instead of the global root
534
// in case we have a different hierarchy. (e.g. export)
535
if (path.charAt(0) == IPath.SEPARATOR)
536             return (IEclipsePreferences) calculateRoot().node(path.substring(1));
537
538         int index = path.indexOf(IPath.SEPARATOR);
539         String JavaDoc key = index == -1 ? path : path.substring(0, index);
540         boolean added = false;
541         IEclipsePreferences child = getChild(key, context, true);
542         if (child == null) {
543             child = create(this, key, context);
544             added = true;
545         }
546         // notify listeners if a child was added
547
if (added && notify)
548             fireNodeEvent(new NodeChangeEvent(this, child), true);
549         return (IEclipsePreferences) child.node(index == -1 ? EMPTY_STRING : path.substring(index + 1));
550     }
551
552     /**
553      * Stores the given (key,value) pair, performing lazy initialization of the
554      * properties field if necessary. Returns the old value for the given key,
555      * or null if no value existed.
556      */

557     protected String JavaDoc internalPut(String JavaDoc key, String JavaDoc newValue) {
558         // illegal state if this node has been removed
559
checkRemoved();
560         String JavaDoc oldValue = properties.get(key);
561         if (oldValue != null && oldValue.equals(newValue))
562             return oldValue;
563         if (DEBUG_PREFERENCE_SET)
564             PrefsMessages.message("Setting preference: " + absolutePath() + '/' + key + '=' + newValue); //$NON-NLS-1$
565
properties = properties.put(key, newValue);
566         return oldValue;
567     }
568
569     /*
570      * Subclasses to over-ride.
571      */

572     protected boolean isAlreadyLoaded(IEclipsePreferences node) {
573         return true;
574     }
575
576     /*
577      * @see org.osgi.service.prefs.Preferences#keys()
578      */

579     public String JavaDoc[] keys() {
580         // illegal state if this node has been removed
581
checkRemoved();
582         return properties.keys();
583     }
584
585     protected void load() throws BackingStoreException {
586         load(getLocation());
587     }
588
589     protected static Properties loadProperties(IPath location) throws BackingStoreException {
590         if (DEBUG_PREFERENCE_GENERAL)
591             PrefsMessages.message("Loading preferences from file: " + location); //$NON-NLS-1$
592
InputStream input = null;
593         Properties result = new Properties();
594         try {
595             input = new BufferedInputStream(new FileInputStream(location.toFile()));
596             result.load(input);
597         } catch (FileNotFoundException e) {
598             // file doesn't exist but that's ok.
599
if (DEBUG_PREFERENCE_GENERAL)
600                 PrefsMessages.message("Preference file does not exist: " + location); //$NON-NLS-1$
601
return result;
602         } catch (IOException e) {
603             String JavaDoc message = NLS.bind(PrefsMessages.preferences_loadException, location);
604             log(new Status(IStatus.INFO, PrefsMessages.OWNER_NAME, IStatus.INFO, message, e));
605             throw new BackingStoreException(message);
606         } finally {
607             if (input != null)
608                 try {
609                     input.close();
610                 } catch (IOException e) {
611                     // ignore
612
}
613         }
614         return result;
615     }
616
617     protected void load(IPath location) throws BackingStoreException {
618         if (location == null) {
619             if (DEBUG_PREFERENCE_GENERAL)
620                 PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
621
return;
622         }
623         Properties fromDisk = loadProperties(location);
624         convertFromProperties(this, fromDisk, false);
625     }
626
627     protected void loaded() {
628         // do nothing
629
}
630
631     protected void loadLegacy() {
632         // sub-classes to over-ride if necessary
633
}
634
635     public static void log(IStatus status) {
636         RuntimeLog.log(status);
637     }
638
639     protected void makeDirty() {
640         EclipsePreferences node = this;
641         while (node != null && !node.removed) {
642             node.dirty = true;
643             node = (EclipsePreferences) node.parent();
644         }
645     }
646
647     public boolean isDirty() {
648         return dirty;
649     }
650
651     /*
652      * @see org.osgi.service.prefs.Preferences#name()
653      */

654     public String JavaDoc name() {
655         return name;
656     }
657
658     /*
659      * @see org.osgi.service.prefs.Preferences#node(java.lang.String)
660      */

661     public Preferences node(String JavaDoc pathName) {
662         return internalNode(pathName, true, null);
663     }
664
665     protected void fireNodeEvent(final NodeChangeEvent event, final boolean added) {
666         if (nodeChangeListeners == null)
667             return;
668         Object JavaDoc[] listeners = nodeChangeListeners.getListeners();
669         for (int i = 0; i < listeners.length; i++) {
670             final INodeChangeListener listener = (INodeChangeListener) listeners[i];
671             ISafeRunnable job = new ISafeRunnable() {
672                 public void handleException(Throwable JavaDoc exception) {
673                     // already logged in Platform#run()
674
}
675
676                 public void run() throws Exception JavaDoc {
677                     if (added)
678                         listener.added(event);
679                     else
680                         listener.removed(event);
681                 }
682             };
683             SafeRunner.run(job);
684         }
685     }
686
687     /*
688      * @see org.osgi.service.prefs.Preferences#nodeExists(java.lang.String)
689      */

690     public boolean nodeExists(String JavaDoc path) throws BackingStoreException {
691         // short circuit for checking this node
692
if (path.length() == 0)
693             return !removed;
694
695         // illegal state if this node has been removed.
696
// do this AFTER checking for the empty string.
697
checkRemoved();
698
699         // use the root relative to this node instead of the global root
700
// in case we have a different hierarchy. (e.g. export)
701
if (path.charAt(0) == IPath.SEPARATOR)
702             return calculateRoot().nodeExists(path.substring(1));
703
704         int index = path.indexOf(IPath.SEPARATOR);
705         boolean noSlash = index == -1;
706
707         // if we are looking for a simple child then just look in the table and return
708
if (noSlash)
709             return childExists(path);
710
711         // otherwise load the parent of the child and then recursively ask
712
String JavaDoc childName = path.substring(0, index);
713         if (!childExists(childName))
714             return false;
715         IEclipsePreferences child = getChild(childName, null, true);
716         if (child == null)
717             return false;
718         return child.nodeExists(path.substring(index + 1));
719     }
720
721     /*
722      * @see org.osgi.service.prefs.Preferences#parent()
723      */

724     public Preferences parent() {
725         // illegal state if this node has been removed
726
checkRemoved();
727         return parent;
728     }
729
730     /*
731      * Convenience method for notifying preference change listeners.
732      */

733     protected void firePreferenceEvent(String JavaDoc key, Object JavaDoc oldValue, Object JavaDoc newValue) {
734         if (preferenceChangeListeners == null)
735             return;
736         Object JavaDoc[] listeners = preferenceChangeListeners.getListeners();
737         final PreferenceChangeEvent event = new PreferenceChangeEvent(this, key, oldValue, newValue);
738         for (int i = 0; i < listeners.length; i++) {
739             final IPreferenceChangeListener listener = (IPreferenceChangeListener) listeners[i];
740             ISafeRunnable job = new ISafeRunnable() {
741                 public void handleException(Throwable JavaDoc exception) {
742                     // already logged in Platform#run()
743
}
744
745                 public void run() throws Exception JavaDoc {
746                     listener.preferenceChange(event);
747                 }
748             };
749             SafeRunner.run(job);
750         }
751     }
752
753     /*
754      * @see org.osgi.service.prefs.Preferences#put(java.lang.String, java.lang.String)
755      */

756     public void put(String JavaDoc key, String JavaDoc newValue) {
757         if (key == null || newValue == null)
758             throw new NullPointerException JavaDoc();
759         String JavaDoc oldValue = internalPut(key, newValue);
760         if (!newValue.equals(oldValue)) {
761             makeDirty();
762             firePreferenceEvent(key, oldValue, newValue);
763         }
764     }
765
766     /*
767      * @see org.osgi.service.prefs.Preferences#putBoolean(java.lang.String, boolean)
768      */

769     public void putBoolean(String JavaDoc key, boolean value) {
770         if (key == null)
771             throw new NullPointerException JavaDoc();
772         String JavaDoc newValue = value ? TRUE : FALSE;
773         String JavaDoc oldValue = internalPut(key, newValue);
774         if (!newValue.equals(oldValue)) {
775             makeDirty();
776             firePreferenceEvent(key, oldValue, newValue);
777         }
778     }
779
780     /*
781      * @see org.osgi.service.prefs.Preferences#putByteArray(java.lang.String, byte[])
782      */

783     public void putByteArray(String JavaDoc key, byte[] value) {
784         if (key == null || value == null)
785             throw new NullPointerException JavaDoc();
786         String JavaDoc newValue = new String JavaDoc(Base64.encode(value));
787         String JavaDoc oldValue = internalPut(key, newValue);
788         if (!newValue.equals(oldValue)) {
789             makeDirty();
790             firePreferenceEvent(key, oldValue, newValue);
791         }
792     }
793
794     /*
795      * @see org.osgi.service.prefs.Preferences#putDouble(java.lang.String, double)
796      */

797     public void putDouble(String JavaDoc key, double value) {
798         if (key == null)
799             throw new NullPointerException JavaDoc();
800         String JavaDoc newValue = Double.toString(value);
801         String JavaDoc oldValue = internalPut(key, newValue);
802         if (!newValue.equals(oldValue)) {
803             makeDirty();
804             firePreferenceEvent(key, oldValue, newValue);
805         }
806     }
807
808     /*
809      * @see org.osgi.service.prefs.Preferences#putFloat(java.lang.String, float)
810      */

811     public void putFloat(String JavaDoc key, float value) {
812         if (key == null)
813             throw new NullPointerException JavaDoc();
814         String JavaDoc newValue = Float.toString(value);
815         String JavaDoc oldValue = internalPut(key, newValue);
816         if (!newValue.equals(oldValue)) {
817             makeDirty();
818             firePreferenceEvent(key, oldValue, newValue);
819         }
820     }
821
822     /*
823      * @see org.osgi.service.prefs.Preferences#putInt(java.lang.String, int)
824      */

825     public void putInt(String JavaDoc key, int value) {
826         if (key == null)
827             throw new NullPointerException JavaDoc();
828         String JavaDoc newValue = Integer.toString(value);
829         String JavaDoc oldValue = internalPut(key, newValue);
830         if (!newValue.equals(oldValue)) {
831             makeDirty();
832             firePreferenceEvent(key, oldValue, newValue);
833         }
834     }
835
836     /*
837      * @see org.osgi.service.prefs.Preferences#putLong(java.lang.String, long)
838      */

839     public void putLong(String JavaDoc key, long value) {
840         if (key == null)
841             throw new NullPointerException JavaDoc();
842         String JavaDoc newValue = Long.toString(value);
843         String JavaDoc oldValue = internalPut(key, newValue);
844         if (!newValue.equals(oldValue)) {
845             makeDirty();
846             firePreferenceEvent(key, oldValue, newValue);
847         }
848     }
849
850     /*
851      * @see org.osgi.service.prefs.Preferences#remove(java.lang.String)
852      */

853     public void remove(String JavaDoc key) {
854         String JavaDoc oldValue = properties.get(key);
855         if (oldValue == null)
856             return;
857         properties = properties.removeKey(key);
858         makeDirty();
859         firePreferenceEvent(key, oldValue, null);
860     }
861
862     /*
863      * @see org.osgi.service.prefs.Preferences#removeNode()
864      */

865     public void removeNode() throws BackingStoreException {
866         // illegal state if this node has been removed
867
checkRemoved();
868         // clear all the property values. do it "the long way" so
869
// everyone gets notification
870
String JavaDoc[] keys = keys();
871         for (int i = 0; i < keys.length; i++)
872             remove(keys[i]);
873         // don't remove the global root or the scope root from the
874
// parent but remove all its children
875
if (parent != null && !(parent instanceof RootPreferences)) {
876             // remove the node from the parent's collection and notify listeners
877
removed = true;
878             parent.removeNode(this);
879         }
880         IEclipsePreferences[] childNodes = getChildren(false);
881         for (int i = 0; i < childNodes.length; i++)
882             try {
883                 childNodes[i].removeNode();
884             } catch (IllegalStateException JavaDoc e) {
885                 // ignore since we only get this exception if we have already
886
// been removed. no work to do.
887
}
888     }
889
890     /*
891      * Remove the child from the collection and notify the listeners if something
892      * was actually removed.
893      */

894     protected void removeNode(IEclipsePreferences child) {
895         boolean wasRemoved = false;
896         synchronized (this) {
897             if (children != null) {
898                 wasRemoved = children.remove(child.name()) != null;
899                 if (wasRemoved)
900                     makeDirty();
901                 if (children.isEmpty())
902                     children = null;
903             }
904         }
905         if (wasRemoved)
906             fireNodeEvent(new NodeChangeEvent(this, child), false);
907     }
908
909     /*
910      * Remove non-initialized node from the collection.
911      */

912     protected void removeNode(String JavaDoc key) {
913         synchronized (this) {
914             if (children != null) {
915                 boolean wasRemoved = children.remove(key) != null;
916                 if (wasRemoved)
917                     makeDirty();
918                 if (children.isEmpty())
919                     children = null;
920             }
921         }
922     }
923
924     /*
925      * @see org.eclipse.core.runtime.IEclipsePreferences#removeNodeChangeListener(org.eclipse.core.runtime.IEclipsePreferences.removeNodeChangeListener)
926      */

927     public void removeNodeChangeListener(INodeChangeListener listener) {
928         checkRemoved();
929         if (nodeChangeListeners == null)
930             return;
931         nodeChangeListeners.remove(listener);
932         if (nodeChangeListeners.size() == 0)
933             nodeChangeListeners = null;
934         if (DEBUG_PREFERENCE_GENERAL)
935             PrefsMessages.message("Removed preference node change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
936
}
937
938     /*
939      * @see org.eclipse.core.runtime.IEclipsePreferences#removePreferenceChangeListener(org.eclipse.core.runtime.IEclipsePreferences.IPreferenceChangeListener)
940      */

941     public void removePreferenceChangeListener(IPreferenceChangeListener listener) {
942         checkRemoved();
943         if (preferenceChangeListeners == null)
944             return;
945         preferenceChangeListeners.remove(listener);
946         if (preferenceChangeListeners.size() == 0)
947             preferenceChangeListeners = null;
948         if (DEBUG_PREFERENCE_GENERAL)
949             PrefsMessages.message("Removed preference property change listener: " + listener + " from: " + absolutePath()); //$NON-NLS-1$ //$NON-NLS-2$
950
}
951
952     protected void save() throws BackingStoreException {
953         save(getLocation());
954     }
955
956     protected void save(IPath location) throws BackingStoreException {
957         if (location == null) {
958             if (DEBUG_PREFERENCE_GENERAL)
959                 PrefsMessages.message("Unable to determine location of preference file for node: " + absolutePath()); //$NON-NLS-1$
960
return;
961         }
962         if (DEBUG_PREFERENCE_GENERAL)
963             PrefsMessages.message("Saving preferences to file: " + location); //$NON-NLS-1$
964
Properties table = convertToProperties(new Properties(), EMPTY_STRING);
965         if (table.isEmpty()) {
966             // nothing to save. delete existing file if one exists.
967
if (location.toFile().exists() && !location.toFile().delete()) {
968                 String JavaDoc message = NLS.bind(PrefsMessages.preferences_failedDelete, location);
969                 log(new Status(IStatus.WARNING, PrefsMessages.OWNER_NAME, IStatus.WARNING, message, null));
970             }
971             return;
972         }
973         table.put(VERSION_KEY, VERSION_VALUE);
974         OutputStream output = null;
975         FileOutputStream fos = null;
976         try {
977             // create the parent dirs if they don't exist
978
File parentFile = location.toFile().getParentFile();
979             if (parentFile == null)
980                 return;
981             parentFile.mkdirs();
982             // set append to be false so we overwrite current settings.
983
fos = new FileOutputStream(location.toOSString(), false);
984             output = new BufferedOutputStream(fos);
985             table.store(output, null);
986             output.flush();
987             fos.getFD().sync();
988         } catch (IOException e) {
989             String JavaDoc message = NLS.bind(PrefsMessages.preferences_saveException, location);
990             log(new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e));
991             throw new BackingStoreException(message);
992         } finally {
993             if (output != null)
994                 try {
995                     output.close();
996                 } catch (IOException e) {
997                     // ignore
998
}
999         }
1000    }
1001
1002    /**
1003     * Traverses the preference hierarchy rooted at this node, and adds
1004     * all preference key and value strings to the provided pool. If an added
1005     * string was already in the pool, all references will be replaced with the
1006     * canonical copy of the string.
1007     *
1008     * @param pool The pool to share strings in
1009     */

1010    public void shareStrings(StringPool pool) {
1011        properties.shareStrings(pool);
1012        IEclipsePreferences[] myChildren = getChildren(false);
1013        for (int i = 0; i < myChildren.length; i++)
1014            if (myChildren[i] instanceof EclipsePreferences)
1015                ((EclipsePreferences) myChildren[i]).shareStrings(pool);
1016    }
1017
1018    /*
1019     * Encode the given path and key combo to a form which is suitable for
1020     * persisting or using when searching. If the key contains a slash character
1021     * then we must use a double-slash to indicate the end of the
1022     * path/the beginning of the key.
1023     */

1024    public static String JavaDoc encodePath(String JavaDoc path, String JavaDoc key) {
1025        String JavaDoc result;
1026        int pathLength = path == null ? 0 : path.length();
1027        if (key.indexOf(IPath.SEPARATOR) == -1) {
1028            if (pathLength == 0)
1029                result = key;
1030            else
1031                result = path + IPath.SEPARATOR + key;
1032        } else {
1033            if (pathLength == 0)
1034                result = DOUBLE_SLASH + key;
1035            else
1036                result = path + DOUBLE_SLASH + key;
1037        }
1038        return result;
1039    }
1040
1041    /*
1042     * Return the segment from the given path or null.
1043     * "segment" parameter is 0-based.
1044     */

1045    public static String JavaDoc getSegment(String JavaDoc path, int segment) {
1046        int start = path.indexOf(IPath.SEPARATOR) == 0 ? 1 : 0;
1047        int end = path.indexOf(IPath.SEPARATOR, start);
1048        if (end == path.length() - 1)
1049            end = -1;
1050        for (int i = 0; i < segment; i++) {
1051            if (end == -1)
1052                return null;
1053            start = end + 1;
1054            end = path.indexOf(IPath.SEPARATOR, start);
1055        }
1056        if (end == -1)
1057            end = path.length();
1058        return path.substring(start, end);
1059    }
1060
1061    public static int getSegmentCount(String JavaDoc path) {
1062        StringTokenizer tokenizer = new StringTokenizer(path, String.valueOf(IPath.SEPARATOR));
1063        return tokenizer.countTokens();
1064    }
1065
1066    /*
1067     * Return a relative path
1068     */

1069    public static String JavaDoc makeRelative(String JavaDoc path) {
1070        String JavaDoc result = path;
1071        if (path == null)
1072            return EMPTY_STRING;
1073        if (path.length() > 0 && path.charAt(0) == IPath.SEPARATOR)
1074            result = path.length() == 0 ? EMPTY_STRING : path.substring(1);
1075        return result;
1076    }
1077
1078    /*
1079     * Return a 2 element String array.
1080     * element 0 - the path
1081     * element 1 - the key
1082     * The path may be null.
1083     * The key is never null.
1084     */

1085    public static String JavaDoc[] decodePath(String JavaDoc fullPath) {
1086        String JavaDoc key = null;
1087        String JavaDoc path = null;
1088
1089        // check to see if we have an indicator which tells us where the path ends
1090
int index = fullPath.indexOf(DOUBLE_SLASH);
1091        if (index == -1) {
1092            // we don't have a double-slash telling us where the path ends
1093
// so the path is up to the last slash character
1094
int lastIndex = fullPath.lastIndexOf(IPath.SEPARATOR);
1095            if (lastIndex == -1) {
1096                key = fullPath;
1097            } else {
1098                path = fullPath.substring(0, lastIndex);
1099                key = fullPath.substring(lastIndex + 1);
1100            }
1101        } else {
1102            // the child path is up to the double-slash and the key
1103
// is the string after it
1104
path = fullPath.substring(0, index);
1105            key = fullPath.substring(index + 2);
1106        }
1107
1108        // adjust if we have an absolute path
1109
if (path != null)
1110            if (path.length() == 0)
1111                path = null;
1112            else if (path.charAt(0) == IPath.SEPARATOR)
1113                path = path.substring(1);
1114
1115        return new String JavaDoc[] {path, key};
1116    }
1117
1118    /*
1119     * @see org.osgi.service.prefs.Preferences#sync()
1120     */

1121
1122    public void sync() throws BackingStoreException {
1123        // illegal state if this node has been removed
1124
checkRemoved();
1125        IEclipsePreferences node = getLoadLevel();
1126        if (node == null) {
1127            if (DEBUG_PREFERENCE_GENERAL)
1128                PrefsMessages.message("Preference node is not a load root: " + absolutePath()); //$NON-NLS-1$
1129
return;
1130        }
1131        if (node instanceof EclipsePreferences) {
1132            ((EclipsePreferences) node).load();
1133            node.flush();
1134        }
1135    }
1136
1137    public String JavaDoc toDeepDebugString() {
1138        final StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
1139        IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
1140            public boolean visit(IEclipsePreferences node) throws BackingStoreException {
1141                buffer.append(node);
1142                buffer.append('\n');
1143                String JavaDoc[] keys = node.keys();
1144                for (int i = 0; i < keys.length; i++) {
1145                    buffer.append(node.absolutePath());
1146                    buffer.append(PATH_SEPARATOR);
1147                    buffer.append(keys[i]);
1148                    buffer.append('=');
1149                    buffer.append(node.get(keys[i], "*default*")); //$NON-NLS-1$
1150
buffer.append('\n');
1151                }
1152                return true;
1153            }
1154        };
1155        try {
1156            accept(visitor);
1157        } catch (BackingStoreException e) {
1158            System.out.println("Exception while calling #toDeepDebugString()"); //$NON-NLS-1$
1159
e.printStackTrace();
1160        }
1161        return buffer.toString();
1162    }
1163
1164    public String JavaDoc toString() {
1165        return absolutePath();
1166    }
1167}
1168
Popular Tags