KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > core > runtime > Preferences


1 /*******************************************************************************
2  * Copyright (c) 2000, 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.runtime;
12
13 import java.io.*;
14 import java.util.*;
15 import org.eclipse.core.internal.preferences.PreferencesService;
16 import org.eclipse.core.internal.preferences.PrefsMessages;
17 import org.eclipse.core.runtime.preferences.*;
18 import org.eclipse.osgi.util.NLS;
19
20 /**
21  * A table of preference settings, mapping named properties to values. Property
22  * names are non-empty strings; property values can be either booleans,
23  * non-null strings, or values of one of the primitive number types.
24  * The table consists of two, sparse, layers: the lower layer holds default values
25  * for properties, and the upper layer holds explicitly set values for properties.
26  * Normal retrieval looks for an explicitly set value for the given property in
27  * the upper layer; if there is nothing for that property in the upper layer, it
28  * next looks for a default value for the given property in the lower layer; if
29  * there is nothing for that property in the lower layer, it returns a standard
30  * default-default value. The default-default values for the primitive types are
31  * as follows:
32  * <ul>
33  * <li><code>boolean</code> = <code>false</code></li>
34  * <li><code>double</code> = <code>0.0</code></li>
35  * <li><code>float</code> = <code>0.0f</code></li>
36  * <li><code>int</code> = <code>0</code></li>
37  * <li><code>long</code> = <code>0L</code></li>
38  * <li><code>String</code> = <code>""</code> (the empty string)</li>
39  * </ul>
40  * <p>
41  * Internally, all properties values (in both layers) are stored as strings.
42  * Standard conversions to and from numeric and boolean types are performed on
43  * demand.
44  * </p>
45  * <p>
46  * The typical usage is to establish the defaults for all known properties
47  * and then restore previously stored values for properties whose values
48  * were explicitly set. The existing settings can be changed and new properties
49  * can be set (<code>setValue</code>). If the values specified is the same as
50  * the default value, the explicit setting is deleted from the top layer.
51  * It is also possible to reset a property value back to the default value
52  * using <code>setToDefault</code>. After the properties have been modified,
53  * the properties with explicit settings are written to disk. The default values
54  * are never saved. This two-tiered approach
55  * to saving and restoring property setting minimizes the number of properties
56  * that need to be persisted; indeed, the normal starting state does not require
57  * storing any properties at all. It also makes it easy to use different
58  * default settings in different environments while maintaining just those
59  * property settings the user has adjusted.
60  * </p>
61  * <p>
62  * A property change event is reported whenever a property's value actually
63  * changes (either through <code>setValue</code>, <code>setToDefault</code>).
64  * Note, however, that manipulating default values (with <code>setDefault</code>)
65  * does not cause any events to be reported.
66  * </p>
67  * <p>
68  * Clients may instantiate this class. This class was not designed to be
69  * subclassed.
70  * </p>
71  * <p>
72  * The implementation is based on a pair of internal
73  * <code>java.util.Properties</code> objects, one holding explicitly set values
74  * (set using <code>setValue</code>), the other holding the default values
75  * (set using <code>setDefaultValue</code>). The <code>load</code> and
76  * <code>store</code> methods persist the non-default property values to
77  * streams (the default values are not saved).
78  * </p>
79  * <p>
80  * If a client sets a default value to be equivalent to the default-default for that
81  * type, the value is still known to the preference store as having a default value.
82  * That is, the name will still be returned in the result of the <code>defaultPropertyNames</code>
83  * and <code>contains</code> methods.
84  * </p>
85  *
86  * @since 2.0
87  */

88 public class Preferences {
89
90     /**
91      * The default-default value for boolean properties (<code>false</code>).
92      */

93     public static final boolean BOOLEAN_DEFAULT_DEFAULT = false;
94
95     /**
96      * The default-default value for double properties (<code>0.0</code>).
97      */

98     public static final double DOUBLE_DEFAULT_DEFAULT = 0.0;
99
100     /**
101      * The default-default value for float properties (<code>0.0f</code>).
102      */

103     public static final float FLOAT_DEFAULT_DEFAULT = 0.0f;
104
105     /**
106      * The default-default value for int properties (<code>0</code>).
107      */

108     public static final int INT_DEFAULT_DEFAULT = 0;
109
110     /**
111      * The default-default value for long properties (<code>0L</code>).
112      */

113     public static final long LONG_DEFAULT_DEFAULT = 0L;
114
115     /**
116      * The default-default value for String properties (<code>""</code>).
117      */

118     public static final String JavaDoc STRING_DEFAULT_DEFAULT = ""; //$NON-NLS-1$
119

120     /**
121      * The string representation used for <code>true</code>
122      * (<code>"true"</code>).
123      */

124     protected static final String JavaDoc TRUE = "true"; //$NON-NLS-1$
125

126     /**
127      * The string representation used for <code>false</code>
128      * (<code>"false"</code>).
129      */

130     protected static final String JavaDoc FALSE = "false"; //$NON-NLS-1$
131

132     /**
133      * Singleton empty string array (optimization)
134      */

135     private static final String JavaDoc[] EMPTY_STRING_ARRAY = new String JavaDoc[0];
136
137     /**
138      * The simple identifier constant (value "<code>preferences</code>") of
139      * the extension point of the Core Runtime plug-in where plug-ins declare
140      * extensions to the preference facility. A plug-in may define any number
141      * of preference extensions.
142      *
143      * @since 3.2
144      */

145     public static final String JavaDoc PT_PREFERENCES = "preferences"; //$NON-NLS-1$
146

147     /**
148      * An event object describing a change to a named property.
149      * <p>
150      * The preferences object reports property change events for internal state
151      * changes that may be of interest to external parties. A special listener
152      * interface (<code>Preferences.IPropertyChangeListener</code>) is
153      * defined for this purpose. Listeners are registered via the
154      * <code>Preferences.addPropertyChangeListener</code> method.
155      * </p>
156      * <p>
157      * Clients cannot instantiate or subclass this class.
158      * </p>
159      *
160      * @see Preferences#addPropertyChangeListener(Preferences.IPropertyChangeListener)
161      * @see Preferences.IPropertyChangeListener
162      */

163     public static class PropertyChangeEvent extends EventObject {
164         /**
165          * All serializable objects should have a stable serialVersionUID
166          */

167         private static final long serialVersionUID = 1L;
168
169         /**
170          * The name of the changed property.
171          */

172         private String JavaDoc propertyName;
173
174         /**
175          * The old value of the changed property, or <code>null</code> if
176          * not known or not relevant.
177          */

178         private Object JavaDoc oldValue;
179
180         /**
181          * The new value of the changed property, or <code>null</code> if
182          * not known or not relevant.
183          */

184         private Object JavaDoc newValue;
185
186         /**
187          * Creates a new property change event.
188          *
189          * @param source the object whose property has changed
190          * @param property the property that has changed (must not be
191          * <code>null</code>)
192          * @param oldValue the old value of the property, or
193          * <code>null</code> if none
194          * @param newValue the new value of the property, or
195          * <code>null</code> if none
196          */

197         protected PropertyChangeEvent(Object JavaDoc source, String JavaDoc property, Object JavaDoc oldValue, Object JavaDoc newValue) {
198
199             super(source);
200             if (property == null) {
201                 throw new IllegalArgumentException JavaDoc();
202             }
203             this.propertyName = property;
204             this.oldValue = oldValue;
205             this.newValue = newValue;
206         }
207
208         /**
209          * Returns the name of the property that changed.
210          * <p>
211          * Warning: there is no guarantee that the property name returned
212          * is a constant string. Callers must compare property names using
213          * <code>equals</code>, not ==.
214          *</p>
215          *
216          * @return the name of the property that changed
217          */

218         public String JavaDoc getProperty() {
219             return propertyName;
220         }
221
222         /**
223          * Returns the new value of the property.
224          *
225          * @return the new value, or <code>null</code> if not known
226          * or not relevant
227          */

228         public Object JavaDoc getNewValue() {
229             return newValue;
230         }
231
232         /**
233          * Returns the old value of the property.
234          *
235          * @return the old value, or <code>null</code> if not known
236          * or not relevant
237          */

238         public Object JavaDoc getOldValue() {
239             return oldValue;
240         }
241     }
242
243     /**
244      * Listener for property changes.
245      * <p>
246      * Usage:
247      * <pre>
248      * Preferences.IPropertyChangeListener listener =
249      * new Preferences.IPropertyChangeListener() {
250      * public void propertyChange(Preferences.PropertyChangeEvent event) {
251      * ... // code to deal with occurrence of property change
252      * }
253      * };
254      * emitter.addPropertyChangeListener(listener);
255      * ...
256      * emitter.removePropertyChangeListener(listener);
257      * </pre>
258      * </p>
259      * <p>
260      * <em>Note:</em> Depending on the means in which the property
261      * values changed, the old and new values for the property can
262      * be either typed, a string representation of the value, or <code>null</code>.
263      * Clients who wish to behave properly in all cases should all
264      * three cases in their implementation of the property change listener.
265      * </p>
266      */

267     public interface IPropertyChangeListener extends EventListener {
268
269         /**
270          * Notification that a property has changed.
271          * <p>
272          * This method gets called when the observed object fires a property
273          * change event.
274          * </p>
275          *
276          * @param event the property change event object describing which
277          * property changed and how
278          */

279         public void propertyChange(Preferences.PropertyChangeEvent event);
280     }
281
282     /**
283      * List of registered listeners (element type:
284      * <code>IPropertyChangeListener</code>).
285      * These listeners are to be informed when the current value of a property
286      * changes.
287      */

288     protected ListenerList listeners = new ListenerList();
289
290     /**
291      * The mapping from property name to
292      * property value (represented as strings).
293      */

294     private Properties properties;
295
296     /**
297      * The mapping from property name to
298      * default property value (represented as strings);
299      * <code>null</code> if none.
300      */

301     private Properties defaultProperties;
302
303     /**
304      * Indicates whether a value has been changed by <code>setToDefault</code>
305      * or <code>setValue</code>; initially <code>false</code>.
306      */

307     protected boolean dirty = false;
308
309     /**
310      * Exports all non-default-valued preferences for all installed plugins to the
311      * provided file. If a file already exists at the given location, it will be deleted.
312      * If there are no preferences to export, no file will be written.
313      * <p>
314      * The file that is written can be read later using the importPreferences method.
315      * </p>
316      * @param path The absolute file system path of the file to export preferences to.
317      * @exception CoreException if this method fails. Reasons include:
318      * <ul>
319      * <li> The file could not be written.</li>
320      * </ul>
321      * @see #importPreferences(IPath)
322      * @see #validatePreferenceVersions(IPath)
323      */

324     public static void exportPreferences(IPath path) throws CoreException {
325         File file = path.toFile();
326         if (file.exists())
327             file.delete();
328         file.getParentFile().mkdirs();
329         IPreferencesService service = PreferencesService.getDefault();
330         OutputStream output = null;
331         FileOutputStream fos = null;
332         try {
333             fos = new FileOutputStream(file);
334             output = new BufferedOutputStream(fos);
335             IEclipsePreferences node = (IEclipsePreferences) service.getRootNode().node(InstanceScope.SCOPE);
336             service.exportPreferences(node, output, (String JavaDoc[]) null);
337             output.flush();
338             fos.getFD().sync();
339         } catch (IOException e) {
340             String JavaDoc message = NLS.bind(PrefsMessages.preferences_errorWriting, file, e.getMessage());
341             IStatus status = new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, IStatus.ERROR, message, e);
342             throw new CoreException(status);
343         } finally {
344             if (output != null)
345                 try {
346                     output.close();
347                 } catch (IOException e) {
348                     // ignore
349
}
350         }
351     }
352
353     /**
354      * Loads the plugin preferences from the given file, and replaces all
355      * non-default-valued preferences for all plugins with the values from this file.
356      * <p>
357      * If the file contains preferences for plug-ins that don't exist in the current
358      * install, they are ignored. This method does not validate if the plug-in
359      * versions in the preference file match the currently installed plug-ins.
360      * Clients should first call validatePreferenceVersions on the file to ensure
361      * that the versions are compatible.
362      * </p>
363      * <p>
364      * The file must have been written by the exportPreferences method.
365      * </p>
366      * @param path The absolute file system path of the file to import preferences from.
367      * @exception CoreException if this method fails. Reasons include:
368      * <ul>
369      * <li> The file does not exist.</li>
370      * <li> The file could not be read.</li>
371      * </ul>
372      * @see #exportPreferences(IPath)
373      * @see #validatePreferenceVersions(IPath)
374      */

375     public static void importPreferences(IPath path) throws CoreException {
376         if (!path.toFile().exists()) {
377             String JavaDoc msg = NLS.bind(PrefsMessages.preferences_fileNotFound, path.toOSString());
378             throw new CoreException(new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, 1, msg, null));
379         }
380         IPreferencesService service = PreferencesService.getDefault();
381         InputStream input = null;
382         try {
383             input = new BufferedInputStream(new FileInputStream(path.toFile()));
384             service.importPreferences(input);
385         } catch (FileNotFoundException e) {
386             String JavaDoc msg = NLS.bind(PrefsMessages.preferences_fileNotFound, path.toOSString());
387             throw new CoreException(new Status(IStatus.ERROR, PrefsMessages.OWNER_NAME, 1, msg, e));
388         } finally {
389             if (input != null)
390                 try {
391                     input.close();
392                 } catch (IOException e) {
393                     // ignore
394
}
395         }
396     }
397
398     /**
399      * Validates that the preference versions in the given file match the versions
400      * of the currently installed plugins. Returns an OK status if all preferences match
401      * the currently installed plugins, otherwise a MultiStatus describing what
402      * plugins have preferences that don't match.
403      * <p>
404      * If the returned status has a <code>IStatus.WARNING</code> severity,
405      * it means that some preferences may not be applicable but for the most
406      * part they will be compatible. If the returned status has a
407      * <code>IStatus.ERROR</code> severity, it means that the preferences
408      * will probably not be compatible.
409      * <p>
410      * If the file contains preferences for plug-ins that don't exist in the current
411      * install, they are ignored.
412      * </p>
413      * <p>
414      * The file must have been written by the exportPreferences method.
415      * </p>
416      * @param file The absolute file system path of the preference file to validate.
417      * @see #exportPreferences(IPath)
418      * @see #importPreferences(IPath)
419      */

420     public static IStatus validatePreferenceVersions(IPath file) {
421         PreferencesService service = PreferencesService.getDefault();
422         return service.validateVersions(file);
423     }
424
425     /**
426      * Creates an empty preference table.
427      * <p>
428      * Use the methods <code>load(InputStream)</code> and
429      * <code>store(InputStream)</code> to load and store these preferences.
430      * </p>
431      * @see #load(InputStream)
432      * @see #store(OutputStream, String)
433      */

434     public Preferences() {
435         defaultProperties = new Properties();
436         properties = new Properties(defaultProperties);
437     }
438
439     /**
440      * Adds a property change listener to this preference object.
441      * Has no affect if the identical listener is already registered.
442      * <p>
443      * <em>Note:</em> Depending on the means in which the property
444      * values changed, the old and new values for the property can
445      * be either typed, a string representation of the value, or <code>null</code>.
446      * Clients who wish to behave properly in all cases should all
447      * three cases in their implementation of the property change listener.
448      * </p>
449      * @param listener a property change listener
450      */

451     public void addPropertyChangeListener(IPropertyChangeListener listener) {
452         listeners.add(listener);
453     }
454
455     /**
456      * Removes the given listener from this preference object.
457      * Has no affect if the listener is not registered.
458      *
459      * @param listener a property change listener
460      */

461     public void removePropertyChangeListener(IPropertyChangeListener listener) {
462         listeners.remove(listener);
463     }
464
465     /**
466      * Returns whether the given property is known to this preference object,
467      * either by having an explicit setting or by having a default
468      * setting. Returns <code>false</code> if the given name is <code>null</code>.
469      *
470      * @param name the name of the property, or <code>null</code>
471      * @return <code>true</code> if either a current value or a default
472      * value is known for the named property, and <code>false</code>otherwise
473      */

474     public boolean contains(String JavaDoc name) {
475         return (properties.containsKey(name) || defaultProperties.containsKey(name));
476     }
477
478     /**
479      * Fires a property change event corresponding to a change to the
480      * current value of the property with the given name.
481      *
482      * @param name the name of the property, to be used as the property
483      * in the event object
484      * @param oldValue the old value, or <code>null</code> if not known or not
485      * relevant
486      * @param newValue the new value, or <code>null</code> if not known or not
487      * relevant
488      */

489     protected void firePropertyChangeEvent(String JavaDoc name, Object JavaDoc oldValue, Object JavaDoc newValue) {
490         if (name == null)
491             throw new IllegalArgumentException JavaDoc();
492         Object JavaDoc[] changeListeners = this.listeners.getListeners();
493         // Do we even need to fire an event?
494
if (changeListeners.length == 0)
495             return;
496         final PropertyChangeEvent pe = new PropertyChangeEvent(this, name, oldValue, newValue);
497         for (int i = 0; i < changeListeners.length; ++i) {
498             final IPropertyChangeListener l = (IPropertyChangeListener) changeListeners[i];
499             ISafeRunnable job = new ISafeRunnable() {
500                 public void handleException(Throwable JavaDoc exception) {
501                     // already being logged in SafeRunner#run()
502
}
503
504                 public void run() throws Exception JavaDoc {
505                     l.propertyChange(pe);
506                 }
507             };
508             SafeRunner.run(job);
509         }
510     }
511
512     /**
513      * Returns the current value of the boolean-valued property with the
514      * given name.
515      * Returns the default-default value (<code>false</code>) if there
516      * is no property with the given name, or if the current value
517      * cannot be treated as a boolean.
518      * The given name must not be <code>null</code>.
519      *
520      * @param name the name of the property
521      * @return the boolean-valued property
522      */

523     public boolean getBoolean(String JavaDoc name) {
524         String JavaDoc value = properties.getProperty(name);
525         if (value == null) {
526             return BOOLEAN_DEFAULT_DEFAULT;
527         }
528         return value.equals(Preferences.TRUE);
529     }
530
531     /**
532      * Sets the current value of the boolean-valued property with the
533      * given name. The given name must not be <code>null</code>.
534      * <p>
535      * A property change event is reported if the current value of the
536      * property actually changes from its previous value. In the event
537      * object, the property name is the name of the property, and the
538      * old and new values are wrapped as objects.
539      * </p>
540      * <p>
541      * If the given value is the same as the corresponding default value
542      * for the given property, the explicit setting is deleted.
543      * Note that the recommended way of re-initializing a property to its
544      * default value is to call <code>setToDefault</code>.
545      * </p>
546      *
547      * @param name the name of the property
548      * @param value the new current value of the property
549      */

550     public void setValue(String JavaDoc name, boolean value) {
551         boolean defaultValue = getDefaultBoolean(name);
552         boolean oldValue = getBoolean(name);
553         if (value == defaultValue) {
554             Object JavaDoc removed = properties.remove(name);
555             if (removed != null) {
556                 // removed an explicit setting
557
dirty = true;
558             }
559         } else {
560             properties.put(name, value ? Preferences.TRUE : Preferences.FALSE);
561         }
562         if (oldValue != value) {
563             // mark as dirty since value did really change
564
dirty = true;
565             // report property change if getValue now returns different value
566
firePropertyChangeEvent(name, oldValue ? Boolean.TRUE : Boolean.FALSE, value ? Boolean.TRUE : Boolean.FALSE);
567         }
568     }
569
570     /**
571      * Returns the default value for the boolean-valued property
572      * with the given name.
573      * Returns the default-default value (<code>false</code>) if there
574      * is no default property with the given name, or if the default
575      * value cannot be treated as a boolean.
576      * The given name must not be <code>null</code>.
577      *
578      * @param name the name of the property
579      * @return the default value of the named property
580      */

581     public boolean getDefaultBoolean(String JavaDoc name) {
582         String JavaDoc value = defaultProperties.getProperty(name);
583         if (value == null) {
584             return BOOLEAN_DEFAULT_DEFAULT;
585         }
586         return value.equals(Preferences.TRUE);
587     }
588
589     /**
590      * Sets the default value for the boolean-valued property with the
591      * given name. The given name must not be <code>null</code>.
592      * <p>
593      * Note that the current value of the property is affected if
594      * the property's current value was its old default value, in which
595      * case it changes to the new default value. If the property's current
596      * is different from its old default value, its current value is
597      * unaffected. No property change events are reported by changing default
598      * values.
599      * </p>
600      *
601      * @param name the name of the property
602      * @param value the new default value for the property
603      */

604     public void setDefault(String JavaDoc name, boolean value) {
605         defaultProperties.put(name, value ? Preferences.TRUE : Preferences.FALSE);
606     }
607
608     /**
609      * Returns the current value of the double-valued property with the
610      * given name.
611      * Returns the default-default value (<code>0.0</code>) if there
612      * is no property with the given name, or if the current value
613      * cannot be treated as a double.
614      * The given name must not be <code>null</code>.
615      *
616      * @param name the name of the property
617      * @return the double-valued property
618      */

619     public double getDouble(String JavaDoc name) {
620         return convertToDouble(properties.getProperty(name), DOUBLE_DEFAULT_DEFAULT);
621     }
622
623     /**
624      * Sets the current value of the double-valued property with the
625      * given name. The given name must not be <code>null</code>.
626      * <p>
627      * A property change event is reported if the current value of the
628      * property actually changes from its previous value. In the event
629      * object, the property name is the name of the property, and the
630      * old and new values are wrapped as objects.
631      * </p>
632      * <p>
633      * If the given value is the same as the corresponding default value
634      * for the given property, the explicit setting is deleted.
635      * Note that the recommended way of re-initializing a property to its
636      * default value is to call <code>setToDefault</code>.
637      * </p>
638      *
639      * @param name the name of the property
640      * @param value the new current value of the property; must be
641      * a number (not a NaN)
642      */

643     public void setValue(String JavaDoc name, double value) {
644         if (Double.isNaN(value)) {
645             throw new IllegalArgumentException JavaDoc();
646         }
647         double defaultValue = getDefaultDouble(name);
648         double oldValue = getDouble(name);
649         if (value == defaultValue) {
650             Object JavaDoc removed = properties.remove(name);
651             if (removed != null) {
652                 // removed an explicit setting
653
dirty = true;
654             }
655         } else {
656             properties.put(name, Double.toString(value));
657         }
658         if (oldValue != value) {
659             // mark as dirty since value did really change
660
dirty = true;
661             // report property change if getValue now returns different value
662
firePropertyChangeEvent(name, new Double JavaDoc(oldValue), new Double JavaDoc(value));
663         }
664     }
665
666     /**
667      * Returns the default value for the double-valued property
668      * with the given name.
669      * Returns the default-default value (<code>0.0</code>) if there
670      * is no default property with the given name, or if the default
671      * value cannot be treated as a double.
672      * The given name must not be <code>null</code>.
673      *
674      * @param name the name of the property
675      * @return the default value of the named property
676      */

677     public double getDefaultDouble(String JavaDoc name) {
678         return convertToDouble(defaultProperties.getProperty(name), DOUBLE_DEFAULT_DEFAULT);
679     }
680
681     /**
682      * Sets the default value for the double-valued property with the
683      * given name. The given name must not be <code>null</code>.
684      * <p>
685      * Note that the current value of the property is affected if
686      * the property's current value was its old default value, in which
687      * case it changes to the new default value. If the property's current
688      * is different from its old default value, its current value is
689      * unaffected. No property change events are reported by changing default
690      * values.
691      * </p>
692      *
693      * @param name the name of the property
694      * @param value the new default value for the property; must be
695      * a number (not a NaN)
696      */

697     public void setDefault(String JavaDoc name, double value) {
698         if (Double.isNaN(value)) {
699             throw new IllegalArgumentException JavaDoc();
700         }
701         defaultProperties.put(name, Double.toString(value));
702     }
703
704     /**
705      * Converts the given raw property value string to a double.
706      *
707      * @param rawPropertyValue the raw property value, or <code>null</code>
708      * if none
709      * @param defaultValue the default value
710      * @return the raw value converted to a double, or the given
711      * <code>defaultValue</code> if the raw value is <code>null</code> or
712      * cannot be parsed as a double
713      */

714     private double convertToDouble(String JavaDoc rawPropertyValue, double defaultValue) {
715         double result = defaultValue;
716         if (rawPropertyValue != null) {
717             try {
718                 result = Double.parseDouble(rawPropertyValue);
719             } catch (NumberFormatException JavaDoc e) {
720                 // raw value cannot be treated as one of these
721
}
722         }
723         return result;
724     }
725
726     /**
727      * Returns the current value of the float-valued property with the
728      * given name.
729      * Returns the default-default value (<code>0.0f</code>) if there
730      * is no property with the given name, or if the current value
731      * cannot be treated as a float.
732      * The given name must not be <code>null</code>.
733      *
734      * @param name the name of the property
735      * @return the float-valued property
736      */

737     public float getFloat(String JavaDoc name) {
738         return convertToFloat(properties.getProperty(name), FLOAT_DEFAULT_DEFAULT);
739     }
740
741     /**
742      * Sets the current value of the float-valued property with the
743      * given name. The given name must not be <code>null</code>.
744      * <p>
745      * A property change event is reported if the current value of the
746      * property actually changes from its previous value. In the event
747      * object, the property name is the name of the property, and the
748      * old and new values are wrapped as objects.
749      * </p>
750      * <p>
751      * If the given value is the same as the corresponding default value
752      * for the given property, the explicit setting is deleted.
753      * Note that the recommended way of re-initializing a property to its
754      * default value is to call <code>setToDefault</code>.
755      * </p>
756      *
757      * @param name the name of the property
758      * @param value the new current value of the property; must be
759      * a number (not a NaN)
760      */

761     public void setValue(String JavaDoc name, float value) {
762         if (Float.isNaN(value)) {
763             throw new IllegalArgumentException JavaDoc();
764         }
765         float defaultValue = getDefaultFloat(name);
766         float oldValue = getFloat(name);
767         if (value == defaultValue) {
768             Object JavaDoc removed = properties.remove(name);
769             if (removed != null) {
770                 // removed an explicit setting
771
dirty = true;
772             }
773         } else {
774             properties.put(name, Float.toString(value));
775         }
776         if (oldValue != value) {
777             // mark as dirty since value did really change
778
dirty = true;
779             // report property change if getValue now returns different value
780
firePropertyChangeEvent(name, new Float JavaDoc(oldValue), new Float JavaDoc(value));
781         }
782     }
783
784     /**
785      * Returns the default value for the float-valued property
786      * with the given name.
787      * Returns the default-default value (<code>0.0f</code>) if there
788      * is no default property with the given name, or if the default
789      * value cannot be treated as a float.
790      * The given name must not be <code>null</code>.
791      *
792      * @param name the name of the property
793      * @return the default value of the named property
794      */

795     public float getDefaultFloat(String JavaDoc name) {
796         return convertToFloat(defaultProperties.getProperty(name), FLOAT_DEFAULT_DEFAULT);
797     }
798
799     /**
800      * Sets the default value for the float-valued property with the
801      * given name. The given name must not be <code>null</code>.
802      * <p>
803      * Note that the current value of the property is affected if
804      * the property's current value was its old default value, in which
805      * case it changes to the new default value. If the property's current
806      * is different from its old default value, its current value is
807      * unaffected. No property change events are reported by changing default
808      * values.
809      * </p>
810      *
811      * @param name the name of the property
812      * @param value the new default value for the property; must be
813      * a number (not a NaN)
814      */

815     public void setDefault(String JavaDoc name, float value) {
816         if (Float.isNaN(value)) {
817             throw new IllegalArgumentException JavaDoc();
818         }
819         defaultProperties.put(name, Float.toString(value));
820     }
821
822     /**
823      * Converts the given raw property value string to a float.
824      *
825      * @param rawPropertyValue the raw property value, or <code>null</code>
826      * if none
827      * @param defaultValue the default value
828      * @return the raw value converted to a float, or the given
829      * <code>defaultValue</code> if the raw value is <code>null</code> or
830      * cannot be parsed as a float
831      */

832     private float convertToFloat(String JavaDoc rawPropertyValue, float defaultValue) {
833         float result = defaultValue;
834         if (rawPropertyValue != null) {
835             try {
836                 result = Float.parseFloat(rawPropertyValue);
837             } catch (NumberFormatException JavaDoc e) {
838                 // raw value cannot be treated as one of these
839
}
840         }
841         return result;
842     }
843
844     /**
845      * Returns the current value of the integer-valued property with the
846      * given name.
847      * Returns the default-default value (<code>0</code>) if there
848      * is no property with the given name, or if the current value
849      * cannot be treated as an integer.
850      * The given name must not be <code>null</code>.
851      *
852      * @param name the name of the property
853      * @return the int-valued property
854      */

855     public int getInt(String JavaDoc name) {
856         return convertToInt(properties.getProperty(name), INT_DEFAULT_DEFAULT);
857     }
858
859     /**
860      * Sets the current value of the integer-valued property with the
861      * given name. The given name must not be <code>null</code>.
862      * <p>
863      * A property change event is reported if the current value of the
864      * property actually changes from its previous value. In the event
865      * object, the property name is the name of the property, and the
866      * old and new values are wrapped as objects.
867      * </p>
868      * <p>
869      * If the given value is the same as the corresponding default value
870      * for the given property, the explicit setting is deleted.
871      * Note that the recommended way of re-initializing a property to its
872      * default value is to call <code>setToDefault</code>.
873      * </p>
874      *
875      * @param name the name of the property
876      * @param value the new current value of the property
877      */

878     public void setValue(String JavaDoc name, int value) {
879         int defaultValue = getDefaultInt(name);
880         int oldValue = getInt(name);
881         if (value == defaultValue) {
882             Object JavaDoc removed = properties.remove(name);
883             if (removed != null) {
884                 // removed an explicit setting
885
dirty = true;
886             }
887         } else {
888             properties.put(name, Integer.toString(value));
889         }
890         if (oldValue != value) {
891             // mark as dirty since value did really change
892
dirty = true;
893             // report property change if getValue now returns different value
894
firePropertyChangeEvent(name, new Integer JavaDoc(oldValue), new Integer JavaDoc(value));
895         }
896     }
897
898     /**
899      * Returns the default value for the integer-valued property
900      * with the given name.
901      * Returns the default-default value (<code>0</code>) if there
902      * is no default property with the given name, or if the default
903      * value cannot be treated as an integer.
904      * The given name must not be <code>null</code>.
905      *
906      * @param name the name of the property
907      * @return the default value of the named property
908      */

909     public int getDefaultInt(String JavaDoc name) {
910         return convertToInt(defaultProperties.getProperty(name), INT_DEFAULT_DEFAULT);
911     }
912
913     /**
914      * Sets the default value for the integer-valued property with the
915      * given name. The given name must not be <code>null</code>.
916      * <p>
917      * Note that the current value of the property is affected if
918      * the property's current value was its old default value, in which
919      * case it changes to the new default value. If the property's current
920      * is different from its old default value, its current value is
921      * unaffected. No property change events are reported by changing default
922      * values.
923      * </p>
924      *
925      * @param name the name of the property
926      * @param value the new default value for the property
927      */

928     public void setDefault(String JavaDoc name, int value) {
929         defaultProperties.put(name, Integer.toString(value));
930     }
931
932     /**
933      * Converts the given raw property value string to an int.
934      *
935      * @param rawPropertyValue the raw property value, or <code>null</code>
936      * if none
937      * @param defaultValue the default value
938      * @return the raw value converted to an int, or the given
939      * <code>defaultValue</code> if the raw value is <code>null</code> or
940      * cannot be parsed as an int
941      */

942     private int convertToInt(String JavaDoc rawPropertyValue, int defaultValue) {
943         int result = defaultValue;
944         if (rawPropertyValue != null) {
945             try {
946                 result = Integer.parseInt(rawPropertyValue);
947             } catch (NumberFormatException JavaDoc e) {
948                 // raw value cannot be treated as one of these
949
}
950         }
951         return result;
952     }
953
954     /**
955      * Returns the current value of the long-valued property with the
956      * given name.
957      * Returns the default-default value (<code>0L</code>) if there
958      * is no property with the given name, or if the current value
959      * cannot be treated as a long.
960      * The given name must not be <code>null</code>.
961      *
962      * @param name the name of the property
963      * @return the long-valued property
964      */

965     public long getLong(String JavaDoc name) {
966         return convertToLong(properties.getProperty(name), LONG_DEFAULT_DEFAULT);
967     }
968
969     /**
970      * Sets the current value of the long-valued property with the
971      * given name. The given name must not be <code>null</code>.
972      * <p>
973      * A property change event is reported if the current value of the
974      * property actually changes from its previous value. In the event
975      * object, the property name is the name of the property, and the
976      * old and new values are wrapped as objects.
977      * </p>
978      * <p>
979      * If the given value is the same as the corresponding default value
980      * for the given property, the explicit setting is deleted.
981      * Note that the recommended way of re-initializing a property to its
982      * default value is to call <code>setToDefault</code>.
983      * </p>
984      *
985      * @param name the name of the property
986      * @param value the new current value of the property
987      */

988     public void setValue(String JavaDoc name, long value) {
989         long defaultValue = getDefaultLong(name);
990         long oldValue = getLong(name);
991         if (value == defaultValue) {
992             Object JavaDoc removed = properties.remove(name);
993             if (removed != null) {
994                 // removed an explicit setting
995
dirty = true;
996             }
997         } else {
998             properties.put(name, Long.toString(value));
999         }
1000        if (oldValue != value) {
1001            // mark as dirty since value did really change
1002
dirty = true;
1003            // report property change if getValue now returns different value
1004
firePropertyChangeEvent(name, new Long JavaDoc(oldValue), new Long JavaDoc(value));
1005        }
1006    }
1007
1008    /**
1009     * Returns the default value for the long-valued property
1010     * with the given name.
1011     * Returns the default-default value (<code>0L</code>) if there
1012     * is no default property with the given name, or if the default
1013     * value cannot be treated as a long.
1014     * The given name must not be <code>null</code>.
1015     *
1016     * @param name the name of the property
1017     * @return the default value of the named property
1018     */

1019    public long getDefaultLong(String JavaDoc name) {
1020        return convertToLong(defaultProperties.getProperty(name), LONG_DEFAULT_DEFAULT);
1021    }
1022
1023    /**
1024     * Sets the default value for the long-valued property with the
1025     * given name. The given name must not be <code>null</code>.
1026     * <p>
1027     * Note that the current value of the property is affected if
1028     * the property's current value was its old default value, in which
1029     * case it changes to the new default value. If the property's current
1030     * is different from its old default value, its current value is
1031     * unaffected. No property change events are reported by changing default
1032     * values.
1033     * </p>
1034     *
1035     * @param name the name of the property
1036     * @param value the new default value for the property
1037     */

1038    public void setDefault(String JavaDoc name, long value) {
1039        defaultProperties.put(name, Long.toString(value));
1040    }
1041
1042    /**
1043     * Converts the given raw property value string to a long.
1044     *
1045     * @param rawPropertyValue the raw property value, or <code>null</code>
1046     * if none
1047     * @param defaultValue the default value
1048     * @return the raw value converted to a long, or the given
1049     * <code>defaultValue</code> if the raw value is <code>null</code> or
1050     * cannot be parsed as a long
1051     */

1052    private long convertToLong(String JavaDoc rawPropertyValue, long defaultValue) {
1053        long result = defaultValue;
1054        if (rawPropertyValue != null) {
1055            try {
1056                result = Long.parseLong(rawPropertyValue);
1057            } catch (NumberFormatException JavaDoc e) {
1058                // raw value cannot be treated as one of these
1059
}
1060        }
1061        return result;
1062    }
1063
1064    /**
1065     * Returns the current value of the string-valued property with the
1066     * given name.
1067     * Returns the default-default value (the empty string <code>""</code>)
1068     * if there is no property with the given name.
1069     * The given name must not be <code>null</code>.
1070     *
1071     * @param name the name of the property
1072     * @return the string-valued property
1073     */

1074    public String JavaDoc getString(String JavaDoc name) {
1075        String JavaDoc value = properties.getProperty(name);
1076        return (value != null ? value : STRING_DEFAULT_DEFAULT);
1077    }
1078
1079    /**
1080     * Sets the current value of the string-valued property with the
1081     * given name. The given name must not be <code>null</code>.
1082     * <p>
1083     * A property change event is reported if the current value of the
1084     * property actually changes from its previous value. In the event
1085     * object, the property name is the name of the property, and the
1086     * old and new values are wrapped as objects.
1087     * </p>
1088     * <p>
1089     * If the given value is the same as the corresponding default value
1090     * for the given property, the explicit setting is deleted.
1091     * Note that the recommended way of re-initializing a property to its
1092     * default value is to call <code>setToDefault</code>.
1093     * </p>
1094     *
1095     * @param name the name of the property
1096     * @param value the new current value of the property
1097     */

1098    public void setValue(String JavaDoc name, String JavaDoc value) {
1099        if (value == null) {
1100            throw new IllegalArgumentException JavaDoc();
1101        }
1102        String JavaDoc defaultValue = getDefaultString(name);
1103        String JavaDoc oldValue = getString(name);
1104        if (value.equals(defaultValue)) {
1105            Object JavaDoc removed = properties.remove(name);
1106            if (removed != null) {
1107                // removed an explicit setting
1108
dirty = true;
1109            }
1110        } else {
1111            properties.put(name, value);
1112        }
1113        if (!oldValue.equals(value)) {
1114            // mark as dirty since value did really change
1115
dirty = true;
1116            // report property change if getValue now returns different value
1117
firePropertyChangeEvent(name, oldValue, value);
1118        }
1119    }
1120
1121    /**
1122     * Returns the default value for the string-valued property
1123     * with the given name.
1124     * Returns the default-default value (the empty string <code>""</code>)
1125     * is no default property with the given name, or if the default
1126     * value cannot be treated as a string.
1127     * The given name must not be <code>null</code>.
1128     *
1129     * @param name the name of the property
1130     * @return the default value of the named property
1131     */

1132    public String JavaDoc getDefaultString(String JavaDoc name) {
1133        String JavaDoc value = defaultProperties.getProperty(name);
1134        return (value != null ? value : STRING_DEFAULT_DEFAULT);
1135    }
1136
1137    /**
1138     * Sets the default value for the string-valued property with the
1139     * given name. The given name must not be <code>null</code>.
1140     * <p>
1141     * Note that the current value of the property is affected if
1142     * the property's current value was its old default value, in which
1143     * case it changes to the new default value. If the property's current
1144     * is different from its old default value, its current value is
1145     * unaffected. No property change events are reported by changing default
1146     * values.
1147     * </p>
1148     *
1149     * @param name the name of the property
1150     * @param value the new default value for the property
1151     */

1152    public void setDefault(String JavaDoc name, String JavaDoc value) {
1153        if (value == null) {
1154            throw new IllegalArgumentException JavaDoc();
1155        }
1156        defaultProperties.put(name, value);
1157    }
1158
1159    /**
1160     * Returns whether the property with the given name has the default value in
1161     * virtue of having no explicitly set value.
1162     * Returns <code>false</code> if the given name is <code>null</code>.
1163     *
1164     * @param name the name of the property, or <code>null</code>
1165     * @return <code>true</code> if the property has no explicitly set value,
1166     * and <code>false</code> otherwise (including the case where the property
1167     * is unknown to this object)
1168     */

1169    public boolean isDefault(String JavaDoc name) {
1170        return !properties.containsKey(name);
1171    }
1172
1173    /**
1174     * Sets the current value of the property with the given name back
1175     * to its default value. Has no effect if the property does not have
1176     * its own current value. The given name must not be <code>null</code>.
1177     * <p>
1178     * Note that the recommended way of re-initializing a property to the
1179     * appropriate default value is to call <code>setToDefault</code>.
1180     * This is implemented by removing the named value from the object,
1181     * thereby exposing the default value.
1182     * </p>
1183     * <p>
1184     * A property change event is always reported. In the event
1185     * object, the property name is the name of the property, and the
1186     * old and new values are either strings, or <code>null</code>
1187     * indicating the default-default value.
1188     * </p>
1189     *
1190     * @param name the name of the property
1191     */

1192    public void setToDefault(String JavaDoc name) {
1193        Object JavaDoc oldPropertyValue = properties.remove(name);
1194        if (oldPropertyValue != null) {
1195            dirty = true;
1196        }
1197        String JavaDoc newValue = defaultProperties.getProperty(name, null);
1198        // n.b. newValue == null if there is no default value
1199
// can't determine correct default-default without knowing type
1200
firePropertyChangeEvent(name, oldPropertyValue, newValue);
1201    }
1202
1203    /**
1204     * Returns a list of all properties known to this preference object which
1205     * have current values other than their default value.
1206     *
1207     * @return an array of property names
1208     */

1209    public String JavaDoc[] propertyNames() {
1210        return (String JavaDoc[]) properties.keySet().toArray(EMPTY_STRING_ARRAY);
1211    }
1212
1213    /**
1214     * Returns a list of all properties known to this preference object which
1215     * have an explicit default value set.
1216     *
1217     * @return an array of property names
1218     */

1219    public String JavaDoc[] defaultPropertyNames() {
1220        return (String JavaDoc[]) defaultProperties.keySet().toArray(EMPTY_STRING_ARRAY);
1221    }
1222
1223    /**
1224     * Returns whether the current values in this preference object
1225     * require saving.
1226     *
1227     * @return <code>true</code> if at least one of the properties
1228     * known to this preference object has a current value different from its
1229     * default value, and <code>false</code> otherwise
1230     */

1231    public boolean needsSaving() {
1232        return dirty;
1233    }
1234
1235    /**
1236     * Saves the non-default-valued properties known to this preference object to
1237     * the given output stream using
1238     * <code>Properties.store(OutputStream,String)</code>.
1239     * <p>
1240     * Note that the output is unconditionally written, even when
1241     * <code>needsSaving</code> is <code>false</code>.
1242     * </p>
1243     *
1244     * @param out the output stream
1245     * @param header a comment to be included in the output, or
1246     * <code>null</code> if none
1247     * @exception IOException if there is a problem saving this preference object
1248     * @see Properties#store(OutputStream,String)
1249     */

1250    public void store(OutputStream out, String JavaDoc header) throws IOException {
1251        properties.store(out, header);
1252        dirty = false;
1253    }
1254
1255    /**
1256     * Loads the non-default-valued properties for this preference object from the
1257     * given input stream using
1258     * <code>java.util.Properties.load(InputStream)</code>. Default property
1259     * values are not affected.
1260     *
1261     * @param in the input stream
1262     * @exception IOException if there is a problem loading this preference
1263     * object
1264     * @see java.util.Properties#load(InputStream)
1265     */

1266    public void load(InputStream in) throws IOException {
1267        properties.load(in);
1268        dirty = false;
1269    }
1270}
1271
Popular Tags