1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.util.prefs;
18
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.OutputStream;
22import java.net.MalformedURLException;
23import java.util.ServiceLoader;
24
25/**
26 * An instance of the class {@code Preferences} represents one node in a
27 * preference tree, which provides a mechanism to store and access configuration
28 * data in a hierarchical way. Two hierarchy trees are maintained, one for
29 * system preferences shared by all users and the other for user preferences
30 * specific to the user. {@code Preferences} hierarchy trees and data are stored
31 * in an implementation-dependent back-end.
32 * <p>
33 * Every node has one name and one unique absolute path following the same
34 * notational conventions as directories in a file system. The root node's
35 * name is "", and other node name strings cannot contain the slash character
36 * and cannot be empty. The root node's absolute path is "/", and all other
37 * nodes' absolute paths are constructed in the standard way: &lt;parent's
38 * absolute path&gt; + "/" + &lt;node's name&gt;. Since the set of nodes forms a
39 * tree with the root node at its base, all absolute paths start with the slash
40 * character. Every node has one relative path to each of its ancestors. The
41 * relative path doesn't start with slash: it equals the node's absolute path
42 * with leading substring removed corresponding to the ancestor's absolute path
43 * and a slash.
44 * <p>
45 * Modification to preferences data may be asynchronous, which means that
46 * preference update method calls may return immediately instead of blocking.
47 * The {@code flush()} and {@code sync()} methods force the back-end to
48 * synchronously perform all pending updates, but the implementation is
49 * permitted to perform the modifications on the underlying back-end data
50 * at any time between the moment the request is made and the moment the
51 * {@code flush()} or {@code sync()} method returns. Please note that if the JVM
52 * exits normally, the implementation must assure all modifications are
53 * persisted implicitly.
54 * <p>
55 * When invoking a method that retrieves preferences, the user must provide
56 * a default value. The default value is returned when the preferences cannot
57 * be found or the back-end is unavailable. Some other methods will throw
58 * {@code BackingStoreException} when the back-end is unavailable.
59 * </p>
60 * <p>
61 * Preferences can be exported to and imported from an XML files. These
62 * documents must have an XML DOCTYPE declaration:
63 * <pre>{@code
64 * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
65 * }</pre>
66 * This system URI is not really accessed by network, it is only a
67 * identification string. Visit the DTD location to see the actual format
68 * permitted.
69 * <p>
70 * There must be a concrete {@code PreferencesFactory} type for every concrete
71 * {@code Preferences} type developed. Every J2SE implementation must provide a
72 * default implementation for every supported platform, and must also provide a
73 * means of replacing the default implementation. This implementation uses the
74 * system property {@code java.util.prefs.PreferencesFactory} to determine which
75 * preferences implementation to use.
76 * <p>
77 * The methods of this class are thread-safe. If multiple JVMs are using the
78 * same back-end concurrently, the back-end won't be corrupted, but no other
79 * behavior guarantees are made.
80 *
81 * @see PreferencesFactory
82 *
83 * @since 1.4
84 */
85public abstract class Preferences {
86    /**
87     * Maximum size in characters allowed for a preferences key.
88     */
89    public static final int MAX_KEY_LENGTH = 80;
90
91    /**
92     * Maximum size in characters allowed for a preferences name.
93     */
94    public static final int MAX_NAME_LENGTH = 80;
95
96    /**
97     * Maximum size in characters allowed for a preferences value.
98     */
99    public static final int MAX_VALUE_LENGTH = 8192;
100
101    //factory used to get user/system prefs root
102    private static final PreferencesFactory factory = findPreferencesFactory();
103
104    private static PreferencesFactory findPreferencesFactory() {
105        // Try the system property first...
106        PreferencesFactory result = ServiceLoader.loadFromSystemProperty(PreferencesFactory.class);
107        if (result != null) {
108            return result;
109        }
110        // Then use ServiceLoader for META-INF/services/...
111        for (PreferencesFactory impl : ServiceLoader.load(PreferencesFactory.class)) {
112            return impl;
113        }
114        // Finally return a default...
115        return new FilePreferencesFactoryImpl();
116    }
117
118    /**
119     * Default constructor, for use by subclasses only.
120     */
121    protected Preferences() {
122    }
123
124    /**
125     * Gets the absolute path string of this preference node.
126     *
127     * @return the preference node's absolute path string.
128     */
129    public abstract String absolutePath();
130
131    /**
132     * Returns the names of all children of this node or an empty array if this
133     * node has no children.
134     *
135     * @return the names of all children of this node.
136     * @throws BackingStoreException
137     *             if backing store is unavailable or causes an operation
138     *             failure.
139     * @throws IllegalStateException
140     *             if this node has been removed.
141     */
142    public abstract String[] childrenNames() throws BackingStoreException;
143
144    /**
145     * Removes all preferences of this node.
146     *
147     * @throws BackingStoreException
148     *             if backing store is unavailable or causes an operation
149     *             failure.
150     * @throws IllegalStateException
151     *             if this node has been removed.
152     */
153    public abstract void clear() throws BackingStoreException;
154
155    /**
156     * Exports all of the preferences of this node to a XML document using the
157     * given output stream.
158     * <p>
159     * This XML document uses the UTF-8 encoding and is written according to the
160     * DTD in its DOCTYPE declaration, which is the following:
161     *
162     * <pre>
163     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
164     * </pre>
165     *
166     * <i>Please note that (unlike the methods of this class that don't concern
167     * serialization), this call is not thread-safe.</i>
168     * </p>
169     *
170     * @param ostream
171     *            the output stream to write the XML-formatted data to.
172     * @throws IOException
173     *             if an error occurs while exporting.
174     * @throws BackingStoreException
175     *             if the backing store is unavailable or causes an operation
176     *             failure.
177     * @throws IllegalStateException
178     *             if this node has been removed.
179     */
180    public abstract void exportNode(OutputStream ostream) throws IOException, BackingStoreException;
181
182    /**
183     * Exports all of the preferences of this node and all its descendants to a
184     * XML document using the given output stream.
185     * <p>
186     * This XML document uses the UTF-8 encoding and is written according to the
187     * DTD in its DOCTYPE declaration, which is the following:
188     *
189     * <pre>
190     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
191     * </pre>
192     *
193     * <i>Please note that (unlike the methods of this class that don't concern
194     * serialization), this call is not thread-safe.</i>
195     * </p>
196     *
197     * @param ostream
198     *            the output stream to write the XML-formatted data to.
199     * @throws IOException
200     *             if an error occurs while exporting.
201     * @throws BackingStoreException
202     *             if the backing store is unavailable or causes an operation
203     *             failure.
204     * @throws IllegalStateException
205     *             if this node has been removed.
206     */
207    public abstract void exportSubtree(OutputStream ostream) throws IOException,
208            BackingStoreException;
209
210    /**
211     * Forces all pending updates to this node and its descendants to be
212     * persisted in the backing store.
213     * <p>
214     * If this node has been removed, the invocation of this method only flushes
215     * this node, not its descendants.
216     * </p>
217     *
218     * @throws BackingStoreException
219     *             if the backing store is unavailable or causes an operation
220     *             failure.
221     */
222    public abstract void flush() throws BackingStoreException;
223
224    /**
225     * Gets the {@code String} value mapped to the given key or its default
226     * value if no value is mapped or no backing store is available.
227     * <p>
228     * Some implementations may store default values in backing stores. In this
229     * case, if there is no value mapped to the given key, the stored default
230     * value is returned.
231     * </p>
232     *
233     * @param key
234     *            the preference key.
235     * @param deflt
236     *            the default value, which will be returned if no value is
237     *            mapped to the given key or no backing store is available.
238     * @return the preference value mapped to the given key.
239     * @throws IllegalStateException
240     *             if this node has been removed.
241     * @throws NullPointerException
242     *             if the parameter {@code key} is {@code null}.
243     */
244    public abstract String get(String key, String deflt);
245
246    /**
247     * Gets the {@code boolean} value mapped to the given key or its default
248     * value if no value is mapped, if the backing store is unavailable, or if
249     * the value is invalid.
250     * <p>
251     * The only valid values are the {@code String} "true", which represents
252     * {@code true} and "false", which represents {@code false}, ignoring case.
253     * </p>
254     * <p>
255     * Some implementations may store default values in backing stores. In this
256     * case, if there is no value mapped to the given key, the stored default
257     * value is returned.
258     * </p>
259     *
260     * @param key
261     *            the preference key.
262     * @param deflt
263     *            the default value, which will be returned if no value is
264     *            mapped to the given key, if the backing store is unavailable,
265     *            or if the value is invalid.
266     * @return the boolean value mapped to the given key.
267     * @throws IllegalStateException
268     *             if this node has been removed.
269     * @throws NullPointerException
270     *             if the parameter {@code key} is {@code null}.
271     */
272    public abstract boolean getBoolean(String key, boolean deflt);
273
274    /**
275     * Gets the {@code byte} array value mapped to the given key or its default
276     * value if no value is mapped, if the backing store is unavailable, or if
277     * the value is an invalid string.
278     * <p>
279     * To be valid, the value string must be Base64-encoded binary data. The
280     * Base64 encoding is as defined in <a
281     * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
282     * </p>
283     * <p>
284     * Some implementations may store default values in backing stores. In this
285     * case, if there is no value mapped to the given key, the stored default
286     * value is returned.
287     * </p>
288     *
289     * @param key
290     *            the preference key.
291     * @param deflt
292     *            the default value, which will be returned if no value is
293     *            mapped to the given key, if the backing store is unavailable,
294     *            or if the value is invalid.
295     * @return the byte array value mapped to the given key.
296     * @throws IllegalStateException
297     *             if this node has been removed.
298     * @throws NullPointerException
299     *             if the parameter {@code key} is {@code null}.
300     */
301    public abstract byte[] getByteArray(String key, byte[] deflt);
302
303    /**
304     * Gets the {@code double} value mapped to the given key or its default
305     * value if no value is mapped, if the backing store is unavailable, or if
306     * the value is an invalid string.
307     * <p>
308     * To be valid, the value string must be a string that can be converted to a
309     * {@code double} by {@link Double#parseDouble(String)
310     * Double.parseDouble(String)}.
311     * <p>
312     * Some implementations may store default values in backing stores. In this
313     * case, if there is no value mapped to the given key, the stored default
314     * value is returned.
315     * </p>
316     *
317     * @param key
318     *            the preference key.
319     * @param deflt
320     *            the default value, which will be returned if no value is
321     *            mapped to the given key, if the backing store is unavailable, or if the
322     *            value is invalid.
323     * @return the double value mapped to the given key.
324     * @throws IllegalStateException
325     *             if this node has been removed.
326     * @throws NullPointerException
327     *             if the parameter {@code key} is {@code null}.
328     */
329    public abstract double getDouble(String key, double deflt);
330
331    /**
332     * Gets the {@code float} value mapped to the given key or its default value
333     * if no value is mapped, if the backing store is unavailable, or if the
334     * value is an invalid string.
335     * <p>
336     * To be valid, the value string must be a string that can be converted to a
337     * {@code float} by {@link Float#parseFloat(String)
338     * Float.parseFloat(String)}.
339     * </p>
340     * <p>
341     * Some implementations may store default values in backing stores. In this
342     * case, if there is no value mapped to the given key, the stored default
343     * value is returned.
344     * </p>
345     *
346     * @param key
347     *            the preference key.
348     * @param deflt
349     *            the default value, which will be returned if no value is
350     *            mapped to the given key, if the backing store is unavailable, or if the
351     *            value is invalid.
352     * @return the float value mapped to the given key.
353     * @throws IllegalStateException
354     *             if this node has been removed.
355     * @throws NullPointerException
356     *             if the parameter {@code key} is {@code null}.
357     */
358    public abstract float getFloat(String key, float deflt);
359
360    /**
361     * Gets the {@code int} value mapped to the given key or its default value
362     * if no value is mapped, if the backing store is unavailable, or if the
363     * value is an invalid string.
364     * <p>
365     * To be valid, the value string must be a string that can be converted to
366     * an {@code int} by {@link Integer#parseInt(String)
367     * Integer.parseInt(String)}.
368     * </p>
369     * <p>
370     * Some implementations may store default values in backing stores. In this
371     * case, if there is no value mapped to the given key, the stored default
372     * value is returned.
373     * </p>
374     *
375     * @param key
376     *            the preference key.
377     * @param deflt
378     *            the default value, which will be returned if no value is
379     *            mapped to the given key, if the backing store is unavailable,
380     *            or if the value is invalid.
381     * @return the integer value mapped to the given key.
382     * @throws IllegalStateException
383     *             if this node has been removed.
384     * @throws NullPointerException
385     *             if the parameter {@code key} is {@code null}.
386     */
387    public abstract int getInt(String key, int deflt);
388
389    /**
390     * Gets the {@code long} value mapped to the given key or its default value
391     * if no value is mapped, if the backing store is unavailable, or if the
392     * value is an invalid string.
393     * <p>
394     * To be valid, the value string must be a string that can be converted to a
395     * {@code long} by {@link Long#parseLong(String) Long.parseLong(String)}.
396     * </p>
397     * <p>
398     * Some implementations may store default values in backing stores. In this
399     * case, if there is no value mapped to the given key, the stored default
400     * value is returned.
401     * </p>
402     *
403     * @param key
404     *            the preference key.
405     * @param deflt
406     *            the default value, which will be returned if no value is
407     *            mapped to the given key, if the backing store is unavailable,
408     *            or if the value is invalid.
409     * @return the long value mapped to the given key.
410     * @throws IllegalStateException
411     *             if this node has been removed.
412     * @throws NullPointerException
413     *             if the parameter {@code key} is {@code null}.
414     */
415    public abstract long getLong(String key, long deflt);
416
417    /**
418     * Imports all the preferences from an XML document using the given input
419     * stream.
420     * <p>
421     * This XML document uses the UTF-8 encoding and must be written according
422     * to the DTD in its DOCTYPE declaration, which must be the following:
423     *
424     * <pre>
425     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
426     * </pre>
427     *
428     * <i>Please note that (unlike the methods of this class that don't concern
429     * serialization), this call is not thread-safe.</i>
430     * </p>
431     *
432     * @param istream
433     *            the input stream to read the data from.
434     * @throws InvalidPreferencesFormatException
435     *             if the data read from the given input stream is not from a
436     *             valid XML document.
437     * @throws IOException
438     *             if an error occurs while importing.
439     */
440    public static void importPreferences (InputStream istream) throws InvalidPreferencesFormatException, IOException {
441        if (istream == null){
442            throw new MalformedURLException("Inputstream cannot be null");
443        }
444        XMLParser.importPrefs(istream);
445    }
446
447    /**
448     * Returns whether this is a user preference node.
449     *
450     * @return {@code true}, if this is a user preference node, {@code false} if
451     *         this is a system preference node.
452     */
453    public abstract boolean isUserNode();
454
455    /**
456     * Returns all preference keys stored in this node or an empty array if no
457     * key was found.
458     *
459     * @return the list of all preference keys of this node.
460     * @throws BackingStoreException
461     *             if the backing store is unavailable or causes an operation
462     *             failure.
463     * @throws IllegalStateException
464     *             if this node has been removed.
465     */
466    public abstract String[] keys() throws BackingStoreException;
467
468    /**
469     * Returns the name of this node.
470     *
471     * @return the name of this node.
472     */
473    public abstract String name();
474
475    /**
476     * Returns the preference node with the given path name. The path name can
477     * be relative or absolute. The requested node and its ancestors will
478     * be created if they do not exist.
479     * <p>
480     * The path is treated as relative to this node if it doesn't start with a
481     * slash, otherwise it will be treated as an absolute path.
482     * </p>
483     *
484     * @param path
485     *            the path name of the requested preference node.
486     * @return the requested preference node.
487     * @throws IllegalStateException
488     *             if this node has been removed.
489     * @throws IllegalArgumentException
490     *             if the path name is invalid.
491     * @throws NullPointerException
492     *             if the given path is {@code null}.
493     */
494    public abstract Preferences node(String path);
495
496    /**
497     * Returns whether the preference node with the given path name exists. The
498     * path is treated as relative to this node if it doesn't start with a slash,
499     * otherwise it is treated as an absolute path.
500     * <p>
501     * Please note that if this node has been removed, an invocation of this
502     * node will throw an {@code IllegalStateException} unless the given path is
503     * an empty string, which will return {@code false}.
504     * </p>
505     *
506     * @param path
507     *            the path name of the preference node to query.
508     * @return {@code true}, if the queried preference node exists, {@code false}
509     *         otherwise.
510     * @throws IllegalStateException
511     *             if this node has been removed and the path is not an empty
512     *             string.
513     * @throws IllegalArgumentException
514     *             if the path name is invalid.
515     * @throws NullPointerException
516     *             if the given path is {@code null}.
517     * @throws BackingStoreException
518     *             if the backing store is unavailable or causes an operation
519     *             failure.
520     */
521    public abstract boolean nodeExists(String path) throws BackingStoreException;
522
523    /**
524     * Returns the parent preference node of this node or {@code null} if this
525     * node is the root node.
526     *
527     * @return the parent preference node of this node.
528     * @throws IllegalStateException
529     *             if this node has been removed.
530     */
531    public abstract Preferences parent();
532
533    /**
534     * Adds a new preference to this node using the given key and value or
535     * updates the value if a preference with the given key already exists.
536     *
537     * @param key
538     *            the preference key to be added or updated.
539     * @param value
540     *            the preference value for the given key.
541     * @throws NullPointerException
542     *             if the given key or value is {@code null}.
543     * @throws IllegalArgumentException
544     *             if the given key's length is bigger than {@code
545     *             MAX_KEY_LENGTH} or the value's length is bigger than {@code
546     *             MAX_VALUE_LENGTH}.
547     * @throws IllegalStateException
548     *             if this node has been removed.
549     */
550    public abstract void put(String key, String value);
551
552    /**
553     * Adds a new preference with a {@code boolean} value to this node using the
554     * given key and value or updates the value if a preference with the given
555     * key already exists.
556     *
557     * @param key
558     *            the preference key to be added or updated.
559     * @param value
560     *            the preference {@code boolean} value for the given key.
561     * @throws NullPointerException
562     *             if the given key is {@code null}.
563     * @throws IllegalArgumentException
564     *             if the given key's length is bigger than {@code
565     *             MAX_KEY_LENGTH}.
566     * @throws IllegalStateException
567     *             if this node has been removed.
568     */
569    public abstract void putBoolean(String key, boolean value);
570
571    /**
572     * Adds a new preference to this node using the given key and the string
573     * form of the given value or updates the value if a preference with the
574     * given key already exists.
575     * <p>
576     * The string form of the value is the Base64-encoded binary data of the
577     * given byte array. The Base64 encoding is as defined in <a
578     * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
579     * </p>
580     *
581     * @param key
582     *            the preference key to be added or updated.
583     * @param value
584     *            the preference value for the given key.
585     * @throws NullPointerException
586     *             if the given key or value is {@code null}.
587     * @throws IllegalArgumentException
588     *             if the given key's length is bigger than {@code
589     *             MAX_KEY_LENGTH} or value's length is bigger than three
590     *             quarters of {@code MAX_KEY_LENGTH}.
591     * @throws IllegalStateException
592     *             if this node has been removed.
593     */
594    public abstract void putByteArray(String key, byte[] value);
595
596    /**
597     * Adds a new preference to this node using the given key and {@code double}
598     * value or updates the value if a preference with the
599     * given key already exists.
600     * <p>
601     * The value is stored in its string form, which is the result of invoking
602     * {@link Double#toString(double) Double.toString(double)}.
603     * </p>
604     *
605     * @param key
606     *            the preference key to be added or updated.
607     * @param value
608     *            the preference value for the given key.
609     * @throws NullPointerException
610     *             if the given key is {@code null}.
611     * @throws IllegalArgumentException
612     *             if the given key's length is bigger than {@code
613     *             MAX_KEY_LENGTH}.
614     * @throws IllegalStateException
615     *             if this node has been removed.
616     */
617    public abstract void putDouble(String key, double value);
618
619    /**
620     * Adds a new preference to this node using the given key and {@code float}
621     * value or updates the value if a preference with the
622     * given key already exists.
623     * <p>
624     * The value is stored in its string form, which is the result of invoking
625     * {@link Float#toString(float) Float.toString(float)}.
626     * </p>
627     *
628     * @param key
629     *            the preference key to be added or updated.
630     * @param value
631     *            the preference value for the given key.
632     * @throws NullPointerException
633     *             if the given key is {@code null}.
634     * @throws IllegalArgumentException
635     *             if the given key's length is bigger than {@code
636     *             MAX_KEY_LENGTH}.
637     * @throws IllegalStateException
638     *             if this node has been removed.
639     */
640    public abstract void putFloat(String key, float value);
641
642    /**
643     * Adds a new preference to this node using the given key and {@code int}
644     * value or updates the value if a preference with the
645     * given key already exists.
646     * <p>
647     * The value is stored in its string form, which is the result of invoking
648     * {@link Integer#toString(int) Integer.toString(int)}.
649     * </p>
650     *
651     * @param key
652     *            the preference key to be added or updated.
653     * @param value
654     *            the preference value for the given key.
655     * @throws NullPointerException
656     *             if the given key is {@code null}.
657     * @throws IllegalArgumentException
658     *             if the given key's length is bigger than {@code
659     *             MAX_KEY_LENGTH}.
660     * @throws IllegalStateException
661     *             if this node has been removed.
662     */
663    public abstract void putInt(String key, int value);
664
665    /**
666     * Adds a new preference to this node using the given key and {@code long}
667     * value or updates the value if a preference with the
668     * given key already exists.
669     * <p>
670     * The value is stored in its string form, which is the result of invoking
671     * {@link Long#toString(long) Long.toString(long)}.
672     * </p>
673     *
674     * @param key
675     *            the preference key to be added or updated.
676     * @param value
677     *            the preference value for the given key.
678     * @throws NullPointerException
679     *             if the given key is {@code null}.
680     * @throws IllegalArgumentException
681     *             if the given key's length is bigger than {@code
682     *             MAX_KEY_LENGTH}.
683     * @throws IllegalStateException
684     *             if this node has been removed.
685     */
686    public abstract void putLong(String key, long value);
687
688    /**
689     * Removes the preference mapped to the given key from this node.
690     *
691     * @param key
692     *            the key of the preference to be removed.
693     * @throws NullPointerException
694     *             if the given key is {@code null}.
695     * @throws IllegalStateException
696     *             if this node has been removed.
697     */
698    public abstract void remove(String key);
699
700    /**
701     * Removes this preference node with all its descendants. The removal won't
702     * necessarily be persisted until the method {@code flush()} is invoked.
703     *
704     * @throws BackingStoreException
705     *             if the backing store is unavailable or causes an operation
706     *             failure.
707     * @throws IllegalStateException
708     *             if this node has been removed.
709     * @throws UnsupportedOperationException
710     *             if this is a root node.
711     */
712    public abstract void removeNode() throws BackingStoreException;
713
714    /**
715     * Registers a {@code NodeChangeListener} instance for this node, which will
716     * handle {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired
717     * when a child node has been added to or removed from this node.
718     *
719     * @param ncl
720     *            the listener to be registered.
721     * @throws NullPointerException
722     *             if the given listener is {@code null}.
723     * @throws IllegalStateException
724     *             if this node has been removed.
725     */
726    public abstract void addNodeChangeListener(NodeChangeListener ncl);
727
728    /**
729     * Registers a {@code PreferenceChangeListener} instance for this node,
730     * which will handle {@code PreferenceChangeEvent}s. {@code
731     * PreferenceChangeEvent}s will be fired when a preference has been added
732     * to, removed from, or updated for this node.
733     *
734     * @param pcl
735     *            the listener to be registered.
736     * @throws NullPointerException
737     *             if the given listener is {@code null}.
738     * @throws IllegalStateException
739     *             if this node has been removed.
740     */
741    public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl);
742
743    /**
744     * Removes the given {@code NodeChangeListener} instance from this node.
745     *
746     * @param ncl
747     *            the listener to be removed.
748     * @throws IllegalArgumentException
749     *             if the given listener is {@code null}.
750     * @throws IllegalStateException
751     *             if this node has been removed.
752     */
753    public abstract void removeNodeChangeListener (NodeChangeListener ncl);
754
755    /**
756     * Removes the given {@code PreferenceChangeListener} instance from this
757     * node.
758     *
759     * @param pcl
760     *            the listener to be removed.
761     * @throws IllegalArgumentException
762     *             if the given listener is {@code null}.
763     * @throws IllegalStateException
764     *             if this node has been removed.
765     */
766    public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl);
767
768    /**
769     * Synchronizes the data of this preference node and its descendants with
770     * the back-end preference store. Any changes found in the back-end data
771     * should be reflected in this node and its descendants, and at the same
772     * time any local changes to this node and descendants should be persisted.
773     *
774     * @throws BackingStoreException
775     *             if the backing store is unavailable or causes an operation
776     *             failure.
777     * @throws IllegalStateException
778     *             if this node has been removed.
779     */
780    public abstract void sync() throws BackingStoreException;
781
782    /**
783     * Returns the system preference node for the package of the given class.
784     * The absolute path of the returned node is one slash followed by the given
785     * class's full package name, replacing each period character ('.') with
786     * a slash. For example, the absolute path of the preference associated with
787     * the class Object would be "/java/lang". As a special case, the unnamed
788     * package is associated with a preference node "/&lt;unnamed&gt;". This
789     * method will create the node and its ancestors as needed. Any nodes created
790     * by this method won't necessarily be persisted until the method {@code
791     * flush()} is invoked.
792     *
793     * @param c
794     *            the given class.
795     * @return the system preference node for the package of the given class.
796     * @throws NullPointerException
797     *             if the given class is {@code null}.
798     */
799    public static Preferences systemNodeForPackage (Class<?> c) {
800        return factory.systemRoot().node(getNodeName(c));
801    }
802
803    /**
804     * Returns the root node of the system preference hierarchy.
805     *
806     * @return the system preference hierarchy root node.
807     */
808    public static Preferences systemRoot() {
809        return factory.systemRoot();
810    }
811
812    /**
813     * Returns the user preference node for the package of the given class.
814     * The absolute path of the returned node is one slash followed by the given
815     * class's full package name, replacing each period character ('.') with
816     * a slash. For example, the absolute path of the preference associated with
817     * the class Object would be "/java/lang". As a special case, the unnamed
818     * package is associated with a preference node "/&lt;unnamed&gt;". This
819     * method will create the node and its ancestors as needed. Any nodes created
820     * by this method won't necessarily be persisted until the method {@code
821     * flush()} is invoked.
822     *
823     * @param c
824     *            the given class.
825     * @return the user preference node for the package of the given class.
826     * @throws NullPointerException
827     *             if the given class is {@code null}.
828     */
829    public static Preferences userNodeForPackage (Class<?> c) {
830        return factory.userRoot().node(getNodeName(c));
831    }
832
833    //parse node's absolute path from class instance
834    private static String getNodeName(Class<?> c){
835        Package p = c.getPackage();
836        if (p == null){
837            return "/<unnamed>";
838        }
839        return "/"+p.getName().replace('.', '/');
840    }
841
842    /**
843     * Returns the root node of the user preference hierarchy.
844     *
845     * @return the user preference hierarchy root node.
846     */
847    public static Preferences userRoot() {
848        return factory.userRoot();
849    }
850
851    /**
852     * Returns a string representation of this node. The format is "User/System
853     * Preference Node: " followed by this node's absolute path.
854     *
855     * @return the string representation of this node.
856     */
857    @Override
858    public abstract String toString();
859}
860