KickJava   Java API By Example, From Geeks To Geeks.

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


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

11 package org.eclipse.core.internal.preferences;
12
13 import java.io.*;
14 import java.lang.ref.WeakReference JavaDoc;
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.framework.Bundle;
21 import org.osgi.framework.Constants;
22 import org.osgi.service.prefs.BackingStoreException;
23 import org.osgi.service.prefs.Preferences;
24
25 /**
26  * @since 3.0
27  */

28 public class PreferencesService implements IPreferencesService {
29     /**
30      * The interval between passes over the preference tree to canonicalize
31      * strings.
32      */

33     private static final long STRING_SHARING_INTERVAL = 300000;
34
35     // cheat here and add "project" even though we really shouldn't know about it
36
// because of plug-in dependencies and it being defined in the resources plug-in
37
private static final String JavaDoc[] DEFAULT_DEFAULT_LOOKUP_ORDER = new String JavaDoc[] {"project", //$NON-NLS-1$
38
InstanceScope.SCOPE, //
39
ConfigurationScope.SCOPE, //
40
DefaultScope.SCOPE};
41     private static final char EXPORT_ROOT_PREFIX = '!';
42     private static final char BUNDLE_VERSION_PREFIX = '@';
43     private static final float EXPORT_VERSION = 3;
44     private static final String JavaDoc VERSION_KEY = "file_export_version"; //$NON-NLS-1$
45
private static final String JavaDoc EMPTY_STRING = ""; //$NON-NLS-1$
46

47     private static PreferencesService instance;
48     static final RootPreferences root = new RootPreferences();
49     private static final Map defaultsRegistry = Collections.synchronizedMap(new HashMap());
50     private Object JavaDoc registryHelper = null;
51     private Map defaultScopes = new HashMap();
52
53     /**
54      * The last time analysis was done to remove duplicate strings
55      */

56     private long lastStringSharing = 0;
57
58     /*
59      * Create and return an IStatus object with ERROR severity and the
60      * given message and exception.
61      */

62     private static IStatus createStatusError(String JavaDoc message, Exception JavaDoc e) {
63         return new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
64     }
65
66     /*
67      * Return the instance.
68      */

69     public static PreferencesService getDefault() {
70         if (instance == null)
71             instance = new PreferencesService();
72         return instance;
73     }
74
75     static void log(IStatus status) {
76         RuntimeLog.log(status);
77     }
78
79     PreferencesService() {
80         super();
81         initializeDefaultScopes();
82     }
83
84     /* (non-Javadoc)
85      * @see org.eclipse.core.runtime.preferences.IPreferencesService#applyPreferences(org.eclipse.core.runtime.preferences.IEclipsePreferences, org.eclipse.core.runtime.preferences.IPreferenceFilter[])
86      */

87     public void applyPreferences(IEclipsePreferences tree, IPreferenceFilter[] filters) throws CoreException {
88         if (filters == null || filters.length == 0)
89             return;
90         try {
91             internalApply(tree, filters);
92             //this typically causes a major change to the preference tree, so force string sharing
93
lastStringSharing = 0;
94             shareStrings();
95         } catch (BackingStoreException e) {
96             throw new CoreException(createStatusError(PrefsMessages.preferences_applyProblems, e));
97         }
98     }
99
100     /*
101      * @see org.eclipse.core.runtime.preferences.IPreferencesService#applyPreferences(org.eclipse.core.runtime.preferences.IExportedPreferences)
102      */

103     public IStatus applyPreferences(IExportedPreferences preferences) throws CoreException {
104         // TODO investigate refactoring to merge with new #apply(IEclipsePreferences, IPreferenceFilter[]) APIs
105
if (preferences == null)
106             throw new IllegalArgumentException JavaDoc();
107
108         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
109             PrefsMessages.message("Applying exported preferences: " + ((ExportedPreferences) preferences).toDeepDebugString()); //$NON-NLS-1$
110

111         final MultiStatus result = new MultiStatus(PrefsMessages.OWNER_NAME, IStatus.OK, PrefsMessages.preferences_applyProblems, null);
112
113         IEclipsePreferences modifiedNode = firePreApplyEvent(preferences);
114
115         // create a visitor to apply the given set of preferences
116
IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
117             public boolean visit(IEclipsePreferences node) throws BackingStoreException {
118                 IEclipsePreferences globalNode;
119                 if (node.parent() == null)
120                     globalNode = root;
121                 else
122                     globalNode = (IEclipsePreferences) root.node(node.absolutePath());
123                 ExportedPreferences epNode = (ExportedPreferences) node;
124
125                 // if this node is an export root then we need to remove
126
// it from the global preferences before continuing.
127
boolean removed = false;
128                 if (epNode.isExportRoot()) {
129                     if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
130                         PrefsMessages.message("Found export root: " + epNode.absolutePath()); //$NON-NLS-1$
131
// TODO should only have to do this if any of my children have properties to set
132
globalNode.removeNode();
133                     removed = true;
134                 }
135
136                 // iterate over the preferences in this node and set them
137
// in the global space.
138
String JavaDoc[] keys = epNode.properties.keys();
139                 if (keys.length > 0) {
140                     // if this node was removed then we need to create a new one
141
if (removed)
142                         globalNode = (IEclipsePreferences) root.node(node.absolutePath());
143                     for (int i = 0; i < keys.length; i++) {
144                         String JavaDoc key = keys[i];
145                         // intern strings we import because some people
146
// in their property change listeners use identity
147
// instead of equals. See bug 20193 and 20534.
148
key = key.intern();
149                         String JavaDoc value = node.get(key, null);
150                         if (value != null) {
151                             if (EclipsePreferences.DEBUG_PREFERENCE_SET)
152                                 PrefsMessages.message("Setting: " + globalNode.absolutePath() + '/' + key + '=' + value); //$NON-NLS-1$
153
globalNode.put(key, value);
154                         }
155                     }
156                 }
157
158                 // keep visiting children
159
return true;
160             }
161         };
162
163         try {
164             // start by visiting the root
165
modifiedNode.accept(visitor);
166         } catch (BackingStoreException e) {
167             throw new CoreException(createStatusError(PrefsMessages.preferences_applyProblems, e));
168         }
169
170         // save the preferences
171
try {
172             getRootNode().node(modifiedNode.absolutePath()).flush();
173         } catch (BackingStoreException e) {
174             throw new CoreException(createStatusError(PrefsMessages.preferences_saveProblems, e));
175         }
176
177         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
178             PrefsMessages.message("Current list of all settings: " + ((EclipsePreferences) getRootNode()).toDeepDebugString()); //$NON-NLS-1$
179
//this typically causes a major change to the preference tree, so force string sharing
180
lastStringSharing = 0;
181         shareStrings();
182         return result;
183     }
184
185     private boolean containsKeys(IEclipsePreferences aRoot) throws BackingStoreException {
186         final boolean result[] = new boolean[] {false};
187         IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
188             public boolean visit(IEclipsePreferences node) throws BackingStoreException {
189                 if (node.keys().length != 0)
190                     result[0] = true;
191                 return !result[0];
192             }
193         };
194         aRoot.accept(visitor);
195         return result[0];
196     }
197
198     /*
199      * Convert the given properties file from legacy format to
200      * one which is Eclipse 3.0 compliant.
201      *
202      * Convert the plug-in version indicator entries to export roots.
203      */

204     private Properties convertFromLegacy(Properties properties) {
205         Properties result = new Properties();
206         String JavaDoc prefix = IPath.SEPARATOR + InstanceScope.SCOPE + IPath.SEPARATOR;
207         for (Iterator i = properties.keySet().iterator(); i.hasNext();) {
208             String JavaDoc key = (String JavaDoc) i.next();
209             String JavaDoc value = properties.getProperty(key);
210             if (value != null) {
211                 int index = key.indexOf(IPath.SEPARATOR);
212                 if (index == -1) {
213                     result.put(BUNDLE_VERSION_PREFIX + key, value);
214                     result.put(EXPORT_ROOT_PREFIX + prefix + key, EMPTY_STRING);
215                 } else {
216                     String JavaDoc path = key.substring(0, index);
217                     key = key.substring(index + 1);
218                     result.put(EclipsePreferences.encodePath(prefix + path, key), value);
219                 }
220             }
221         }
222         return result;
223     }
224
225     /*
226      * Convert the given properties file into a node hierarchy suitable for
227      * importing.
228      */

229     private IExportedPreferences convertFromProperties(Properties properties) {
230         IExportedPreferences result = ExportedPreferences.newRoot();
231         for (Iterator i = properties.keySet().iterator(); i.hasNext();) {
232             String JavaDoc path = (String JavaDoc) i.next();
233             String JavaDoc value = properties.getProperty(path);
234             if (path.charAt(0) == EXPORT_ROOT_PREFIX) {
235                 ExportedPreferences current = (ExportedPreferences) result.node(path.substring(1));
236                 current.setExportRoot();
237             } else if (path.charAt(0) == BUNDLE_VERSION_PREFIX) {
238                 ExportedPreferences current = (ExportedPreferences) result.node(InstanceScope.SCOPE).node(path.substring(1));
239                 current.setVersion(value);
240             } else {
241                 String JavaDoc[] decoded = EclipsePreferences.decodePath(path);
242                 path = decoded[0] == null ? EMPTY_STRING : decoded[0];
243                 ExportedPreferences current = (ExportedPreferences) result.node(path);
244                 String JavaDoc key = decoded[1];
245                 current.put(key, value);
246             }
247         }
248         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
249             PrefsMessages.message("Converted preferences file to IExportedPreferences tree: " + ((ExportedPreferences) result).toDeepDebugString()); //$NON-NLS-1$
250
return result;
251     }
252
253     /*
254      * excludesList is guaranteed not to be null
255      */

256     private Properties convertToProperties(IEclipsePreferences preferences, final String JavaDoc[] excludesList) throws BackingStoreException {
257         final Properties result = new Properties();
258         final int baseLength = preferences.absolutePath().length();
259
260         // create a visitor to do the export
261
IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
262             public boolean visit(IEclipsePreferences node) throws BackingStoreException {
263                 // don't store defaults
264
String JavaDoc absolutePath = node.absolutePath();
265                 String JavaDoc scope = getScope(absolutePath);
266                 if (DefaultScope.SCOPE.equals(scope))
267                     return false;
268                 String JavaDoc path = absolutePath.length() <= baseLength ? EMPTY_STRING : EclipsePreferences.makeRelative(absolutePath.substring(baseLength));
269                 // check the excludes list to see if this node should be considered
270
for (int i = 0; i < excludesList.length; i++) {
271                     String JavaDoc exclusion = EclipsePreferences.makeRelative(excludesList[i]);
272                     if (path.startsWith(exclusion))
273                         return false;
274                 }
275                 boolean needToAddVersion = InstanceScope.SCOPE.equals(scope);
276                 // check the excludes list for each preference
277
String JavaDoc[] keys = node.keys();
278                 for (int i = 0; i < keys.length; i++) {
279                     String JavaDoc key = keys[i];
280                     boolean ignore = false;
281                     for (int j = 0; !ignore && j < excludesList.length; j++)
282                         if (EclipsePreferences.encodePath(path, key).startsWith(EclipsePreferences.makeRelative(excludesList[j])))
283                             ignore = true;
284                     if (!ignore) {
285                         String JavaDoc value = node.get(key, null);
286                         if (value != null) {
287                             if (needToAddVersion) {
288                                 String JavaDoc bundle = getBundleName(absolutePath);
289                                 if (bundle != null) {
290                                     String JavaDoc version = getBundleVersion(bundle);
291                                     if (version != null)
292                                         result.put(BUNDLE_VERSION_PREFIX + bundle, version);
293                                 }
294                                 needToAddVersion = false;
295                             }
296                             result.put(EclipsePreferences.encodePath(absolutePath, key), value);
297                         }
298                     }
299                 }
300                 return true;
301             }
302         };
303
304         // start by visiting the root that we were passed in
305
preferences.accept(visitor);
306
307         // return the properties object
308
return result;
309     }
310
311     /**
312      * Copy key/value pairs from the source to the destination. If the key list is null
313      * then copy all associations.
314      *
315      * If the depth is 0, then this operation is performed only on the source node. Otherwise
316      * it is performed on the source node's subtree.
317      *
318      * @param depth one of 0 or -1
319      */

320     void copyFromTo(Preferences source, Preferences destination, String JavaDoc[] keys, int depth) throws BackingStoreException {
321         String JavaDoc[] keysToCopy = keys == null ? source.keys() : keys;
322         for (int i = 0; i < keysToCopy.length; i++) {
323             String JavaDoc value = source.get(keysToCopy[i], null);
324             if (value != null)
325                 destination.put(keysToCopy[i], value);
326         }
327         if (depth == 0)
328             return;
329         String JavaDoc[] children = source.childrenNames();
330         for (int i = 0; i < children.length; i++)
331             copyFromTo(source.node(children[i]), destination.node(children[i]), keys, depth);
332     }
333
334     public WeakReference JavaDoc applyRuntimeDefaults(String JavaDoc name, WeakReference JavaDoc pluginReference) {
335         if (registryHelper == null)
336             return null;
337         return ((PreferenceServiceRegistryHelper) registryHelper).applyRuntimeDefaults(name, pluginReference);
338     }
339
340     private void initializeDefaultScopes() {
341         defaultScopes.put(DefaultScope.SCOPE, new DefaultPreferences());
342         root.addChild(DefaultScope.SCOPE, null);
343         defaultScopes.put(InstanceScope.SCOPE, new InstancePreferences());
344         root.addChild(InstanceScope.SCOPE, null);
345         defaultScopes.put(ConfigurationScope.SCOPE, new ConfigurationPreferences());
346         root.addChild(ConfigurationScope.SCOPE, null);
347     }
348
349     public IEclipsePreferences createNode(String JavaDoc key) {
350         IScope scope = (IScope) defaultScopes.get(key);
351         if (scope == null) {
352             if (registryHelper == null)
353                 return new EclipsePreferences(root, key);
354             return ((PreferenceServiceRegistryHelper) registryHelper).createNode(root, key);
355         }
356         return scope.create(root, key);
357     }
358
359     /* (non-Javadoc)
360      * @see org.eclipse.core.runtime.preferences.IPreferencesService#exportPreferences(IEclipsePreferences, IPreferenceFilter[], OutputStream)
361      */

362     public void exportPreferences(IEclipsePreferences node, IPreferenceFilter[] filters, OutputStream stream) throws CoreException {
363         if (filters == null || filters.length == 0)
364             return;
365         try {
366             internalExport(node, filters, stream);
367         } catch (BackingStoreException e) {
368             throw new CoreException(createStatusError(PrefsMessages.preferences_exportProblems, e));
369         }
370     }
371
372     /*
373      * @see org.eclipse.core.runtime.preferences.IPreferencesService#exportPreferences(org.eclipse.core.runtime.preferences.IEclipsePreferences, java.io.OutputStream, java.lang.String[])
374      */

375     public IStatus exportPreferences(IEclipsePreferences node, OutputStream output, String JavaDoc[] excludesList) throws CoreException {
376         // TODO investigate refactoring to merge with new #export(IEclipsePreferences, IPreferenceFilter[]) APIs
377
if (node == null || output == null)
378             throw new IllegalArgumentException JavaDoc();
379         Properties properties = null;
380         if (excludesList == null)
381             excludesList = new String JavaDoc[0];
382         try {
383             properties = convertToProperties(node, excludesList);
384             if (properties.isEmpty())
385                 return Status.OK_STATUS;
386             properties.put(VERSION_KEY, Float.toString(EXPORT_VERSION));
387             properties.put(EXPORT_ROOT_PREFIX + node.absolutePath(), EMPTY_STRING);
388         } catch (BackingStoreException e) {
389             throw new CoreException(createStatusError(e.getMessage(), e));
390         }
391         try {
392             properties.store(output, null);
393         } catch (IOException e) {
394             throw new CoreException(createStatusError(PrefsMessages.preferences_exportProblems, e));
395         }
396         return Status.OK_STATUS;
397     }
398
399     /*
400      * Give clients a chance to modify the tree before it is applied globally
401      */

402     private IEclipsePreferences firePreApplyEvent(IEclipsePreferences tree) {
403         if (registryHelper == null)
404             return tree;
405         final IEclipsePreferences[] result = new IEclipsePreferences[] {tree};
406         PreferenceModifyListener[] listeners = ((PreferenceServiceRegistryHelper) registryHelper).getModifyListeners();
407         for (int i = 0; i < listeners.length; i++) {
408             final PreferenceModifyListener listener = listeners[i];
409             ISafeRunnable job = new ISafeRunnable() {
410                 public void handleException(Throwable JavaDoc exception) {
411                     // already logged in Platform#run()
412
}
413
414                 public void run() throws Exception JavaDoc {
415                     result[0] = listener.preApply(result[0]);
416                 }
417             };
418             SafeRunner.run(job);
419         }
420         return result[0];
421     }
422
423     /*
424      * @see org.eclipse.core.runtime.preferences.IPreferencesService#get(java.lang.String, java.lang.String, org.osgi.service.prefs.Preferences[])
425      */

426     public String JavaDoc get(String JavaDoc key, String JavaDoc defaultValue, Preferences[] nodes) {
427         if (nodes == null)
428             return defaultValue;
429         for (int i = 0; i < nodes.length; i++) {
430             Preferences node = nodes[i];
431             if (node != null) {
432                 String JavaDoc result = node.get(key, null);
433                 if (result != null)
434                     return result;
435             }
436         }
437         return defaultValue;
438     }
439
440     /*
441      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getBoolean(java.lang.String, java.lang.String, boolean, org.eclipse.core.runtime.preferences.IScope[])
442      */

443     public boolean getBoolean(String JavaDoc qualifier, String JavaDoc key, boolean defaultValue, IScopeContext[] scopes) {
444         String JavaDoc result = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
445         return result == null ? defaultValue : Boolean.valueOf(result).booleanValue();
446     }
447
448     /*
449      * Return the name of the bundle from the given path.
450      * It is assumed that that path is:
451      * - absolute
452      * - in the instance scope
453      */

454     String JavaDoc getBundleName(String JavaDoc path) {
455         if (path.length() == 0 || path.charAt(0) != IPath.SEPARATOR)
456             return null;
457         int first = path.indexOf(IPath.SEPARATOR, 1);
458         if (first == -1)
459             return null;
460         int second = path.indexOf(IPath.SEPARATOR, first + 1);
461         return second == -1 ? path.substring(first + 1) : path.substring(first + 1, second);
462     }
463
464     /*
465      * Return the version for the bundle with the given name. Return null if it
466      * is not known or there is a problem.
467      */

468     String JavaDoc getBundleVersion(String JavaDoc bundleName) {
469         Bundle bundle = PreferencesOSGiUtils.getDefault().getBundle(bundleName);
470         if (bundle != null) {
471             Object JavaDoc version = bundle.getHeaders(EMPTY_STRING).get(Constants.BUNDLE_VERSION);
472             if (version != null && version instanceof String JavaDoc)
473                 return (String JavaDoc) version;
474         }
475         return null;
476     }
477
478     /*
479      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getByteArray(java.lang.String, java.lang.String, byte[], org.eclipse.core.runtime.preferences.IScope[])
480      */

481     public byte[] getByteArray(String JavaDoc qualifier, String JavaDoc key, byte[] defaultValue, IScopeContext[] scopes) {
482         String JavaDoc result = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
483         return result == null ? defaultValue : result.getBytes();
484     }
485
486     /*
487      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getDefaultLookupOrder(java.lang.String, java.lang.String)
488      */

489     public String JavaDoc[] getDefaultLookupOrder(String JavaDoc qualifier, String JavaDoc key) {
490         LookupOrder order = (LookupOrder) defaultsRegistry.get(getRegistryKey(qualifier, key));
491         return order == null ? null : order.getOrder();
492     }
493
494     /*
495      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getDouble(java.lang.String, java.lang.String, double, org.eclipse.core.runtime.preferences.IScope[])
496      */

497     public double getDouble(String JavaDoc qualifier, String JavaDoc key, double defaultValue, IScopeContext[] scopes) {
498         String JavaDoc value = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
499         if (value == null)
500             return defaultValue;
501         try {
502             return Double.parseDouble(value);
503         } catch (NumberFormatException JavaDoc e) {
504             return defaultValue;
505         }
506     }
507
508     /*
509      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getFloat(java.lang.String, java.lang.String, float, org.eclipse.core.runtime.preferences.IScope[])
510      */

511     public float getFloat(String JavaDoc qualifier, String JavaDoc key, float defaultValue, IScopeContext[] scopes) {
512         String JavaDoc value = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
513         if (value == null)
514             return defaultValue;
515         try {
516             return Float.parseFloat(value);
517         } catch (NumberFormatException JavaDoc e) {
518             return defaultValue;
519         }
520     }
521
522     /*
523      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getInt(java.lang.String, java.lang.String, int, org.eclipse.core.runtime.preferences.IScope[])
524      */

525     public int getInt(String JavaDoc qualifier, String JavaDoc key, int defaultValue, IScopeContext[] scopes) {
526         String JavaDoc value = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
527         if (value == null)
528             return defaultValue;
529         try {
530             return Integer.parseInt(value);
531         } catch (NumberFormatException JavaDoc e) {
532             return defaultValue;
533         }
534     }
535
536     /*
537      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getRootNode()
538      */

539
540     /*
541      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getLong(java.lang.String, java.lang.String, long, org.eclipse.core.runtime.preferences.IScope[])
542      */

543     public long getLong(String JavaDoc qualifier, String JavaDoc key, long defaultValue, IScopeContext[] scopes) {
544         String JavaDoc value = get(EclipsePreferences.decodePath(key)[1], null, getNodes(qualifier, key, scopes));
545         if (value == null)
546             return defaultValue;
547         try {
548             return Long.parseLong(value);
549         } catch (NumberFormatException JavaDoc e) {
550             return defaultValue;
551         }
552     }
553
554     /*
555      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getLookupOrder(java.lang.String, java.lang.String)
556      */

557     public String JavaDoc[] getLookupOrder(String JavaDoc qualifier, String JavaDoc key) {
558         String JavaDoc[] order = getDefaultLookupOrder(qualifier, key);
559         // if there wasn't an exact match based on both qualifier and simple name
560
// then do a lookup based only on the qualifier
561
if (order == null && key != null)
562             order = getDefaultLookupOrder(qualifier, null);
563         if (order == null)
564             order = DEFAULT_DEFAULT_LOOKUP_ORDER;
565         return order;
566     }
567
568     private Preferences[] getNodes(String JavaDoc qualifier, String JavaDoc key, IScopeContext[] contexts) {
569         String JavaDoc[] order = getLookupOrder(qualifier, key);
570         String JavaDoc childPath = EclipsePreferences.makeRelative(EclipsePreferences.decodePath(key)[0]);
571         ArrayList result = new ArrayList();
572         for (int i = 0; i < order.length; i++) {
573             String JavaDoc scopeString = order[i];
574             boolean found = false;
575             for (int j = 0; contexts != null && j < contexts.length; j++) {
576                 IScopeContext context = contexts[j];
577                 if (context != null && context.getName().equals(scopeString)) {
578                     Preferences node = context.getNode(qualifier);
579                     if (node != null) {
580                         found = true;
581                         if (childPath != null)
582                             node = node.node(childPath);
583                         result.add(node);
584                     }
585                 }
586             }
587             if (!found) {
588                 Preferences node = getRootNode().node(scopeString).node(qualifier);
589                 if (childPath != null)
590                     node = node.node(childPath);
591                 result.add(node);
592             }
593             found = false;
594         }
595         return (Preferences[]) result.toArray(new Preferences[result.size()]);
596     }
597
598     /*
599      * Convert the given qualifier and key into a key to use in the look-up registry.
600      */

601     private String JavaDoc getRegistryKey(String JavaDoc qualifier, String JavaDoc key) {
602         if (qualifier == null)
603             throw new IllegalArgumentException JavaDoc();
604         if (key == null)
605             return qualifier;
606         return qualifier + '/' + key;
607     }
608
609     public IEclipsePreferences getRootNode() {
610         return root;
611     }
612
613     /*
614      * Return the string which is the scope for the given path.
615      * Return the empty string if it cannot be determined.
616      */

617     String JavaDoc getScope(String JavaDoc path) {
618         if (path == null || path.length() == 0)
619             return EMPTY_STRING;
620         int startIndex = path.indexOf(IPath.SEPARATOR);
621         if (startIndex == -1)
622             return path;
623         if (path.length() == 1)
624             return EMPTY_STRING;
625         int endIndex = path.indexOf(IPath.SEPARATOR, startIndex + 1);
626         if (endIndex == -1)
627             endIndex = path.length();
628         return path.substring(startIndex + 1, endIndex);
629     }
630
631     /*
632      * @see org.eclipse.core.runtime.preferences.IPreferencesService#getString(java.lang.String, java.lang.String, java.lang.String, org.eclipse.core.runtime.preferences.IScope[])
633      */

634     public String JavaDoc getString(String JavaDoc qualifier, String JavaDoc key, String JavaDoc defaultValue, IScopeContext[] scopes) {
635         return get(EclipsePreferences.decodePath(key)[1], defaultValue, getNodes(qualifier, key, scopes));
636     }
637
638     /*
639      * @see org.eclipse.core.runtime.preferences.IPreferencesService#importPreferences(java.io.InputStream)
640      */

641     public IStatus importPreferences(InputStream input) throws CoreException {
642         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
643             PrefsMessages.message("Importing preferences..."); //$NON-NLS-1$
644
return applyPreferences(readPreferences(input));
645     }
646
647     /**
648      * Filter the given tree so it only contains values which apply to the specified filters
649      * then apply the resulting tree to the main preference tree.
650      */

651     private void internalApply(IEclipsePreferences tree, IPreferenceFilter[] filters) throws BackingStoreException {
652         ArrayList trees = new ArrayList();
653         for (int i = 0; i < filters.length; i++)
654             trees.add(trimTree(tree, filters[i]));
655         // merge the union of the matching filters
656
IEclipsePreferences toApply = mergeTrees((IEclipsePreferences[]) trees.toArray(new IEclipsePreferences[trees.size()]));
657
658         // fire an event to give people a chance to modify the tree
659
toApply = firePreApplyEvent(toApply);
660
661         // actually apply the settings
662
IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
663             public boolean visit(IEclipsePreferences node) throws BackingStoreException {
664                 String JavaDoc[] keys = node.keys();
665                 if (keys.length == 0)
666                     return true;
667                 copyFromTo(node, getRootNode().node(node.absolutePath()), keys, 0);
668                 return true;
669             }
670         };
671         toApply.accept(visitor);
672     }
673
674     /**
675      * Take the preference tree and trim it so it only holds values applying to the given filters.
676      * Then export the resulting tree to the given output stream.
677      */

678     private void internalExport(IEclipsePreferences node, IPreferenceFilter filters[], OutputStream output) throws BackingStoreException, CoreException {
679         ArrayList trees = new ArrayList();
680         for (int i = 0; i < filters.length; i++)
681             trees.add(trimTree(node, filters[i]));
682         IEclipsePreferences toExport = mergeTrees((IEclipsePreferences[]) trees.toArray(new IEclipsePreferences[trees.size()]));
683         exportPreferences(toExport, output, (String JavaDoc[]) null);
684     }
685
686     /*
687      * Return true if the given tree contains information that the specified filter is interested
688      * in, and false otherwise.
689      */

690     private boolean internalMatches(IEclipsePreferences tree, IPreferenceFilter filter) throws BackingStoreException {
691         String JavaDoc[] scopes = filter.getScopes();
692         if (scopes == null)
693             throw new IllegalArgumentException JavaDoc();
694         String JavaDoc treePath = tree.absolutePath();
695         // see if this node is applicable by going over all our scopes
696
for (int i = 0; i < scopes.length; i++) {
697             String JavaDoc scope = scopes[i];
698             Map mapping = filter.getMapping(scope);
699             // if the mapping is null then we match everything
700
if (mapping == null) {
701                 // if we are the root check to see if the scope exists
702
if (tree.parent() == null && tree.nodeExists(scope))
703                     return containsKeys((IEclipsePreferences) tree.node(scope));
704                 // otherwise check to see if we are in the right scope
705
if (scopeMatches(scope, tree) && containsKeys(tree))
706                     return true;
707                 continue;
708             }
709             // iterate over the list of declared nodes
710
for (Iterator iter = mapping.keySet().iterator(); iter.hasNext();) {
711                 String JavaDoc nodePath = (String JavaDoc) iter.next();
712                 String JavaDoc nodeFullPath = '/' + scope + '/' + nodePath;
713                 // if this subtree isn't in a hierarchy we are interested in, then go to the next one
714
if (!nodeFullPath.startsWith(treePath))
715                     continue;
716                 // get the child node
717
String JavaDoc childPath = nodeFullPath.substring(treePath.length());
718                 childPath = EclipsePreferences.makeRelative(childPath);
719                 if (tree.nodeExists(childPath)) {
720                     PreferenceFilterEntry[] entries;
721                     // protect against wrong classes since this is user-code
722
try {
723                         entries = (PreferenceFilterEntry[]) mapping.get(nodePath);
724                     } catch (ClassCastException JavaDoc e) {
725                         log(createStatusError(PrefsMessages.preferences_classCastFilterEntry, e));
726                         continue;
727                     }
728                     // if there are no entries defined then we return false even if we
729
// are supposed to match on the existence of the node as a whole (bug 88820)
730
Preferences child = tree.node(childPath);
731                     if (entries == null)
732                         return child.keys().length != 0 || child.childrenNames().length != 0;
733                     // otherwise check to see if we have any applicable keys
734
for (int j = 0; j < entries.length; j++) {
735                         if (entries[j] != null && child.get(entries[j].getKey(), null) != null)
736                             return true;
737                     }
738                 }
739             }
740         }
741         return false;
742     }
743
744     /*
745      * Internal method that collects the matching filters for the given tree and returns them.
746      */

747     private IPreferenceFilter[] internalMatches(IEclipsePreferences tree, IPreferenceFilter[] filters) throws BackingStoreException {
748         ArrayList result = new ArrayList();
749         for (int i = 0; i < filters.length; i++)
750             if (internalMatches(tree, filters[i]))
751                 result.add(filters[i]);
752         return (IPreferenceFilter[]) result.toArray(new IPreferenceFilter[result.size()]);
753     }
754
755     /*
756      * Returns a boolean value indicating whether or not the given Properties
757      * object is the result of a preference export previous to Eclipse 3.0.
758      *
759      * Check the contents of the file. In Eclipse 3.0 we printed out a file
760      * version key.
761      */

762     private boolean isLegacy(Properties properties) {
763         return properties.getProperty(VERSION_KEY) == null;
764     }
765
766     /* (non-Javadoc)
767      * @see IPreferencesService#matches(IEclipsePreferences, IPreferenceFilter[])
768      */

769     public IPreferenceFilter[] matches(IEclipsePreferences tree, IPreferenceFilter[] filters) throws CoreException {
770         if (filters == null || filters.length == 0)
771             return new IPreferenceFilter[0];
772         try {
773             return internalMatches(tree, filters);
774         } catch (BackingStoreException e) {
775             throw new CoreException(createStatusError(PrefsMessages.preferences_matching, e));
776         }
777     }
778
779     private IEclipsePreferences mergeTrees(IEclipsePreferences[] trees) throws BackingStoreException {
780         if (trees.length == 1)
781             return trees[0];
782         final IEclipsePreferences result = ExportedPreferences.newRoot();
783         if (trees.length == 0)
784             return result;
785         IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
786             public boolean visit(IEclipsePreferences node) throws BackingStoreException {
787                 Preferences destination = result.node(node.absolutePath());
788                 copyFromTo(node, destination, null, 0);
789                 return true;
790             }
791         };
792         for (int i = 0; i < trees.length; i++)
793             trees[i].accept(visitor);
794         return result;
795     }
796
797     /*
798      * @see org.eclipse.core.runtime.preferences.IPreferencesService#readPreferences(java.io.InputStream)
799      */

800     public IExportedPreferences readPreferences(InputStream input) throws CoreException {
801         if (input == null)
802             throw new IllegalArgumentException JavaDoc();
803
804         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
805             PrefsMessages.message("Reading preferences from stream..."); //$NON-NLS-1$
806

807         // read the file into a properties object
808
Properties properties = new Properties();
809         try {
810             properties.load(input);
811         } catch (IOException e) {
812             throw new CoreException(createStatusError(PrefsMessages.preferences_importProblems, e));
813         } finally {
814             try {
815                 input.close();
816             } catch (IOException e) {
817                 // ignore
818
}
819         }
820
821         // an empty file is an invalid file format
822
if (properties.isEmpty())
823             throw new CoreException(createStatusError(PrefsMessages.preferences_invalidFileFormat, null));
824
825         // manipulate the file if it from a legacy preference export
826
if (isLegacy(properties)) {
827             if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
828                 PrefsMessages.message("Read legacy preferences file, converting to 3.0 format..."); //$NON-NLS-1$
829
properties = convertFromLegacy(properties);
830         } else {
831             if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
832                 PrefsMessages.message("Read preferences file."); //$NON-NLS-1$
833
properties.remove(VERSION_KEY);
834         }
835
836         // convert the Properties object into an object to return
837
return convertFromProperties(properties);
838     }
839
840     /**
841      * Return true if the given node is in the specified scope and false otherwise.
842      */

843     private boolean scopeMatches(String JavaDoc scope, IEclipsePreferences tree) {
844         // the root isn't in any scope
845
if (tree.parent() == null)
846             return false;
847         // fancy math to get the first segment of the path
848
String JavaDoc path = tree.absolutePath();
849         int index = path.indexOf('/', 1);
850         String JavaDoc sub = path.substring(1, index == -1 ? path.length() : index);
851         return scope.equals(sub);
852     }
853
854     /*
855      * @see org.eclipse.core.runtime.preferences.IPreferencesService#setDefaultLookupOrder(java.lang.String, java.lang.String, java.lang.String[])
856      */

857     public void setDefaultLookupOrder(String JavaDoc qualifier, String JavaDoc key, String JavaDoc[] order) {
858         String JavaDoc registryKey = getRegistryKey(qualifier, key);
859         if (order == null)
860             defaultsRegistry.remove(registryKey);
861         else {
862             LookupOrder obj = new LookupOrder(order);
863             defaultsRegistry.put(registryKey, obj);
864         }
865     }
866
867     public void setRegistryHelper(Object JavaDoc registryHelper) {
868         if (this.registryHelper != null && this.registryHelper != registryHelper)
869             ((PreferenceServiceRegistryHelper) this.registryHelper).stop();
870         this.registryHelper = registryHelper;
871     }
872
873     /**
874      * Shares all duplicate equal strings referenced by the preference service.
875      */

876     void shareStrings() {
877         long now = System.currentTimeMillis();
878         if (now - lastStringSharing < STRING_SHARING_INTERVAL)
879             return;
880         StringPool pool = new StringPool();
881         root.shareStrings(pool);
882         if (EclipsePreferences.DEBUG_PREFERENCE_GENERAL)
883             System.out.println("Preference string sharing saved: " + pool.getSavedStringCount()); //$NON-NLS-1$
884
lastStringSharing = now;
885     }
886
887     /*
888      * Return a tree which contains only nodes and keys which are applicable to the given filter.
889      */

890     private IEclipsePreferences trimTree(IEclipsePreferences tree, IPreferenceFilter filter) throws BackingStoreException {
891         IEclipsePreferences result = (IEclipsePreferences) ExportedPreferences.newRoot().node(tree.absolutePath());
892         String JavaDoc[] scopes = filter.getScopes();
893         if (scopes == null)
894             throw new IllegalArgumentException JavaDoc();
895         String JavaDoc treePath = tree.absolutePath();
896         // see if this node is applicable by going over all our scopes
897
for (int i = 0; i < scopes.length; i++) {
898             String JavaDoc scope = scopes[i];
899             Map mapping = filter.getMapping(scope);
900             // if the mapping is null then copy everything if the scope matches
901
if (mapping == null) {
902                 // if we are the root node then check our children
903
if (tree.parent() == null && tree.nodeExists(scope))
904                     copyFromTo(tree.node(scope), result.node(scope), null, -1);
905                 // ensure we are in the correct scope
906
else if (scopeMatches(scope, tree))
907                     copyFromTo(tree, result, null, -1);
908                 continue;
909             }
910             // iterate over the list of declared nodes
911
for (Iterator iter = mapping.keySet().iterator(); iter.hasNext();) {
912                 String JavaDoc nodePath = (String JavaDoc) iter.next();
913                 String JavaDoc nodeFullPath = '/' + scope + '/' + nodePath;
914                 // if this subtree isn't in a hierarchy we are interested in, then go to the next one
915
if (!nodeFullPath.startsWith(treePath))
916                     continue;
917                 // get the child node
918
String JavaDoc childPath = nodeFullPath.substring(treePath.length());
919                 childPath = EclipsePreferences.makeRelative(childPath);
920                 if (tree.nodeExists(childPath)) {
921                     PreferenceFilterEntry[] entries;
922                     // protect against wrong classes since this is passed in by the user
923
try {
924                         entries = (PreferenceFilterEntry[]) mapping.get(nodePath);
925                     } catch (ClassCastException JavaDoc e) {
926                         log(createStatusError(PrefsMessages.preferences_classCastFilterEntry, e));
927                         continue;
928                     }
929                     String JavaDoc[] keys = null;
930                     if (entries != null) {
931                         ArrayList list = new ArrayList();
932                         for (int j = 0; j < entries.length; j++) {
933                             if (entries[j] != null)
934                                 list.add(entries[j].getKey());
935                         }
936                         keys = (String JavaDoc[]) list.toArray(new String JavaDoc[list.size()]);
937                     }
938                     // do infinite depth if there are no keys specified since the parent matched.
939
copyFromTo(tree.node(childPath), result.node(childPath), keys, keys == null ? -1 : 0);
940                 }
941             }
942         }
943         return result;
944     }
945
946     /**
947      * Compares two plugin version identifiers to see if their preferences
948      * are compatible. If they are not compatible, a warning message is
949      * added to the given multi-status, according to the following rules:
950      *
951      * - plugins that differ in service number: no status
952      * - plugins that differ in minor version: WARNING status
953      * - plugins that differ in major version:
954      * - where installed plugin is newer: WARNING status
955      * - where installed plugin is older: ERROR status
956      * @param bundle the name of the bundle
957      * @param pref The version identifier of the preferences to be loaded
958      * @param installed The version identifier of the installed plugin
959      */

960     IStatus validatePluginVersions(String JavaDoc bundle, PluginVersionIdentifier pref, PluginVersionIdentifier installed) {
961         if (installed.getMajorComponent() == pref.getMajorComponent() && installed.getMinorComponent() == pref.getMinorComponent())
962             return null;
963         int severity;
964         if (installed.getMajorComponent() < pref.getMajorComponent())
965             severity = IStatus.ERROR;
966         else
967             severity = IStatus.WARNING;
968         String JavaDoc msg = NLS.bind(PrefsMessages.preferences_incompatible, (new Object JavaDoc[] {pref, bundle, installed}));
969         return new Status(severity, PrefsMessages.OWNER_NAME, 1, msg, null);
970     }
971
972     public IStatus validateVersions(IPath path) {
973         final MultiStatus result = new MultiStatus(PrefsMessages.OWNER_NAME, IStatus.INFO, PrefsMessages.preferences_validate, null);
974         IPreferenceNodeVisitor visitor = new IPreferenceNodeVisitor() {
975             public boolean visit(IEclipsePreferences node) {
976                 if (!(node instanceof ExportedPreferences))
977                     return false;
978
979                 // calculate the version in the file
980
ExportedPreferences realNode = (ExportedPreferences) node;
981                 String JavaDoc version = realNode.getVersion();
982                 if (version == null || !PluginVersionIdentifier.validateVersion(version).isOK())
983                     return true;
984                 PluginVersionIdentifier versionInFile = new PluginVersionIdentifier(version);
985
986                 // calculate the version of the installed bundle
987
String JavaDoc bundleName = getBundleName(node.absolutePath());
988                 if (bundleName == null)
989                     return true;
990                 String JavaDoc stringVersion = getBundleVersion(bundleName);
991                 if (stringVersion == null || !PluginVersionIdentifier.validateVersion(stringVersion).isOK())
992                     return true;
993                 PluginVersionIdentifier versionInMemory = new PluginVersionIdentifier(stringVersion);
994
995                 // verify the versions based on the matching rules
996
IStatus verification = validatePluginVersions(bundleName, versionInFile, versionInMemory);
997                 if (verification != null)
998                     result.add(verification);
999
1000                return true;
1001            }
1002        };
1003
1004        InputStream input = null;
1005        try {
1006            input = new BufferedInputStream(new FileInputStream(path.toFile()));
1007            IExportedPreferences prefs = readPreferences(input);
1008            prefs.accept(visitor);
1009        } catch (FileNotFoundException e) {
1010            // ignore...if the file does not exist then all is OK
1011
} catch (CoreException e) {
1012            result.add(createStatusError(PrefsMessages.preferences_validationException, e));
1013        } catch (BackingStoreException e) {
1014            result.add(createStatusError(PrefsMessages.preferences_validationException, e));
1015        }
1016        return result;
1017    }
1018
1019}
1020
Popular Tags