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 volatile PreferencesFactory factory = findPreferencesFactory();
103
104    /**
105     * @hide for testing only.
106     */
107    public static PreferencesFactory setPreferencesFactory(PreferencesFactory pf) {
108        PreferencesFactory previous = factory;
109        factory = pf;
110        return previous;
111    }
112
113    private static PreferencesFactory findPreferencesFactory() {
114        // Try the system property first...
115        PreferencesFactory result = ServiceLoader.loadFromSystemProperty(PreferencesFactory.class);
116        if (result != null) {
117            return result;
118        }
119        // Then use ServiceLoader for META-INF/services/...
120        for (PreferencesFactory impl : ServiceLoader.load(PreferencesFactory.class)) {
121            return impl;
122        }
123        // Finally return a default...
124        return new FilePreferencesFactoryImpl();
125    }
126
127    /**
128     * Default constructor, for use by subclasses only.
129     */
130    protected Preferences() {
131    }
132
133    /**
134     * Gets the absolute path string of this preference node.
135     *
136     * @return the preference node's absolute path string.
137     */
138    public abstract String absolutePath();
139
140    /**
141     * Returns the names of all children of this node or an empty array if this
142     * node has no children.
143     *
144     * @return the names of all children of this node.
145     * @throws BackingStoreException
146     *             if backing store is unavailable or causes an operation
147     *             failure.
148     * @throws IllegalStateException
149     *             if this node has been removed.
150     */
151    public abstract String[] childrenNames() throws BackingStoreException;
152
153    /**
154     * Removes all preferences of this node.
155     *
156     * @throws BackingStoreException
157     *             if backing store is unavailable or causes an operation
158     *             failure.
159     * @throws IllegalStateException
160     *             if this node has been removed.
161     */
162    public abstract void clear() throws BackingStoreException;
163
164    /**
165     * Exports all of the preferences of this node to a XML document using the
166     * given output stream.
167     * <p>
168     * This XML document uses the UTF-8 encoding and is written according to the
169     * DTD in its DOCTYPE declaration, which is the following:
170     *
171     * <pre>
172     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
173     * </pre>
174     *
175     * <i>Please note that (unlike the methods of this class that don't concern
176     * serialization), this call is not thread-safe.</i>
177     * </p>
178     *
179     * @param ostream
180     *            the output stream to write the XML-formatted data to.
181     * @throws IOException
182     *             if an error occurs while exporting.
183     * @throws BackingStoreException
184     *             if the backing store is unavailable or causes an operation
185     *             failure.
186     * @throws IllegalStateException
187     *             if this node has been removed.
188     */
189    public abstract void exportNode(OutputStream ostream) throws IOException, BackingStoreException;
190
191    /**
192     * Exports all of the preferences of this node and all its descendants to a
193     * XML document using the given output stream.
194     * <p>
195     * This XML document uses the UTF-8 encoding and is written according to the
196     * DTD in its DOCTYPE declaration, which is the following:
197     *
198     * <pre>
199     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
200     * </pre>
201     *
202     * <i>Please note that (unlike the methods of this class that don't concern
203     * serialization), this call is not thread-safe.</i>
204     * </p>
205     *
206     * @param ostream
207     *            the output stream to write the XML-formatted data to.
208     * @throws IOException
209     *             if an error occurs while exporting.
210     * @throws BackingStoreException
211     *             if the backing store is unavailable or causes an operation
212     *             failure.
213     * @throws IllegalStateException
214     *             if this node has been removed.
215     */
216    public abstract void exportSubtree(OutputStream ostream) throws IOException,
217            BackingStoreException;
218
219    /**
220     * Forces all pending updates to this node and its descendants to be
221     * persisted in the backing store.
222     * <p>
223     * If this node has been removed, the invocation of this method only flushes
224     * this node, not its descendants.
225     * </p>
226     *
227     * @throws BackingStoreException
228     *             if the backing store is unavailable or causes an operation
229     *             failure.
230     */
231    public abstract void flush() throws BackingStoreException;
232
233    /**
234     * Gets the {@code String} value mapped to the given key or its default
235     * value if no value is mapped or no backing store is available.
236     * <p>
237     * Some implementations may store default values in backing stores. In this
238     * case, if there is no value mapped to the given key, the stored default
239     * value is returned.
240     * </p>
241     *
242     * @param key
243     *            the preference key.
244     * @param deflt
245     *            the default value, which will be returned if no value is
246     *            mapped to the given key or no backing store is available.
247     * @return the preference value mapped to the given key.
248     * @throws IllegalStateException
249     *             if this node has been removed.
250     * @throws NullPointerException
251     *             if the parameter {@code key} is {@code null}.
252     */
253    public abstract String get(String key, String deflt);
254
255    /**
256     * Gets the {@code boolean} value mapped to the given key or its default
257     * value if no value is mapped, if the backing store is unavailable, or if
258     * the value is invalid.
259     * <p>
260     * The only valid values are the {@code String} "true", which represents
261     * {@code true} and "false", which represents {@code false}, ignoring case.
262     * </p>
263     * <p>
264     * Some implementations may store default values in backing stores. In this
265     * case, if there is no value mapped to the given key, the stored default
266     * value is returned.
267     * </p>
268     *
269     * @param key
270     *            the preference key.
271     * @param deflt
272     *            the default value, which will be returned if no value is
273     *            mapped to the given key, if the backing store is unavailable,
274     *            or if the value is invalid.
275     * @return the boolean value mapped to the given key.
276     * @throws IllegalStateException
277     *             if this node has been removed.
278     * @throws NullPointerException
279     *             if the parameter {@code key} is {@code null}.
280     */
281    public abstract boolean getBoolean(String key, boolean deflt);
282
283    /**
284     * Gets the {@code byte} array value mapped to the given key or its default
285     * value if no value is mapped, if the backing store is unavailable, or if
286     * the value is an invalid string.
287     * <p>
288     * To be valid, the value string must be Base64-encoded binary data. The
289     * Base64 encoding is as defined in <a
290     * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
291     * </p>
292     * <p>
293     * Some implementations may store default values in backing stores. In this
294     * case, if there is no value mapped to the given key, the stored default
295     * value is returned.
296     * </p>
297     *
298     * @param key
299     *            the preference key.
300     * @param deflt
301     *            the default value, which will be returned if no value is
302     *            mapped to the given key, if the backing store is unavailable,
303     *            or if the value is invalid.
304     * @return the byte array value mapped to the given key.
305     * @throws IllegalStateException
306     *             if this node has been removed.
307     * @throws NullPointerException
308     *             if the parameter {@code key} is {@code null}.
309     */
310    public abstract byte[] getByteArray(String key, byte[] deflt);
311
312    /**
313     * Gets the {@code double} value mapped to the given key or its default
314     * value if no value is mapped, if the backing store is unavailable, or if
315     * the value is an invalid string.
316     * <p>
317     * To be valid, the value string must be a string that can be converted to a
318     * {@code double} by {@link Double#parseDouble(String)
319     * Double.parseDouble(String)}.
320     * <p>
321     * Some implementations may store default values in backing stores. In this
322     * case, if there is no value mapped to the given key, the stored default
323     * value is returned.
324     * </p>
325     *
326     * @param key
327     *            the preference key.
328     * @param deflt
329     *            the default value, which will be returned if no value is
330     *            mapped to the given key, if the backing store is unavailable, or if the
331     *            value is invalid.
332     * @return the double value mapped to the given key.
333     * @throws IllegalStateException
334     *             if this node has been removed.
335     * @throws NullPointerException
336     *             if the parameter {@code key} is {@code null}.
337     */
338    public abstract double getDouble(String key, double deflt);
339
340    /**
341     * Gets the {@code float} value mapped to the given key or its default value
342     * if no value is mapped, if the backing store is unavailable, or if the
343     * value is an invalid string.
344     * <p>
345     * To be valid, the value string must be a string that can be converted to a
346     * {@code float} by {@link Float#parseFloat(String)
347     * Float.parseFloat(String)}.
348     * </p>
349     * <p>
350     * Some implementations may store default values in backing stores. In this
351     * case, if there is no value mapped to the given key, the stored default
352     * value is returned.
353     * </p>
354     *
355     * @param key
356     *            the preference key.
357     * @param deflt
358     *            the default value, which will be returned if no value is
359     *            mapped to the given key, if the backing store is unavailable, or if the
360     *            value is invalid.
361     * @return the float value mapped to the given key.
362     * @throws IllegalStateException
363     *             if this node has been removed.
364     * @throws NullPointerException
365     *             if the parameter {@code key} is {@code null}.
366     */
367    public abstract float getFloat(String key, float deflt);
368
369    /**
370     * Gets the {@code int} value mapped to the given key or its default value
371     * if no value is mapped, if the backing store is unavailable, or if the
372     * value is an invalid string.
373     * <p>
374     * To be valid, the value string must be a string that can be converted to
375     * an {@code int} by {@link Integer#parseInt(String)
376     * Integer.parseInt(String)}.
377     * </p>
378     * <p>
379     * Some implementations may store default values in backing stores. In this
380     * case, if there is no value mapped to the given key, the stored default
381     * value is returned.
382     * </p>
383     *
384     * @param key
385     *            the preference key.
386     * @param deflt
387     *            the default value, which will be returned if no value is
388     *            mapped to the given key, if the backing store is unavailable,
389     *            or if the value is invalid.
390     * @return the integer value mapped to the given key.
391     * @throws IllegalStateException
392     *             if this node has been removed.
393     * @throws NullPointerException
394     *             if the parameter {@code key} is {@code null}.
395     */
396    public abstract int getInt(String key, int deflt);
397
398    /**
399     * Gets the {@code long} value mapped to the given key or its default value
400     * if no value is mapped, if the backing store is unavailable, or if the
401     * value is an invalid string.
402     * <p>
403     * To be valid, the value string must be a string that can be converted to a
404     * {@code long} by {@link Long#parseLong(String) Long.parseLong(String)}.
405     * </p>
406     * <p>
407     * Some implementations may store default values in backing stores. In this
408     * case, if there is no value mapped to the given key, the stored default
409     * value is returned.
410     * </p>
411     *
412     * @param key
413     *            the preference key.
414     * @param deflt
415     *            the default value, which will be returned if no value is
416     *            mapped to the given key, if the backing store is unavailable,
417     *            or if the value is invalid.
418     * @return the long value mapped to the given key.
419     * @throws IllegalStateException
420     *             if this node has been removed.
421     * @throws NullPointerException
422     *             if the parameter {@code key} is {@code null}.
423     */
424    public abstract long getLong(String key, long deflt);
425
426    /**
427     * Imports all the preferences from an XML document using the given input
428     * stream.
429     * <p>
430     * This XML document uses the UTF-8 encoding and must be written according
431     * to the DTD in its DOCTYPE declaration, which must be the following:
432     *
433     * <pre>
434     * &lt;!DOCTYPE preferences SYSTEM &quot;http://java.sun.com/dtd/preferences.dtd">;
435     * </pre>
436     *
437     * <i>Please note that (unlike the methods of this class that don't concern
438     * serialization), this call is not thread-safe.</i>
439     * </p>
440     *
441     * @param istream
442     *            the input stream to read the data from.
443     * @throws InvalidPreferencesFormatException
444     *             if the data read from the given input stream is not from a
445     *             valid XML document.
446     * @throws IOException
447     *             if an error occurs while importing.
448     */
449    public static void importPreferences (InputStream istream) throws InvalidPreferencesFormatException, IOException {
450        if (istream == null){
451            throw new MalformedURLException("Inputstream cannot be null");
452        }
453        XMLParser.importPrefs(istream);
454    }
455
456    /**
457     * Returns whether this is a user preference node.
458     *
459     * @return {@code true}, if this is a user preference node, {@code false} if
460     *         this is a system preference node.
461     */
462    public abstract boolean isUserNode();
463
464    /**
465     * Returns all preference keys stored in this node or an empty array if no
466     * key was found.
467     *
468     * @return the list of all preference keys of this node.
469     * @throws BackingStoreException
470     *             if the backing store is unavailable or causes an operation
471     *             failure.
472     * @throws IllegalStateException
473     *             if this node has been removed.
474     */
475    public abstract String[] keys() throws BackingStoreException;
476
477    /**
478     * Returns the name of this node.
479     *
480     * @return the name of this node.
481     */
482    public abstract String name();
483
484    /**
485     * Returns the preference node with the given path name. The path name can
486     * be relative or absolute. The requested node and its ancestors will
487     * be created if they do not exist.
488     * <p>
489     * The path is treated as relative to this node if it doesn't start with a
490     * slash, otherwise it will be treated as an absolute path.
491     * </p>
492     *
493     * @param path
494     *            the path name of the requested preference node.
495     * @return the requested preference node.
496     * @throws IllegalStateException
497     *             if this node has been removed.
498     * @throws IllegalArgumentException
499     *             if the path name is invalid.
500     * @throws NullPointerException
501     *             if the given path is {@code null}.
502     */
503    public abstract Preferences node(String path);
504
505    /**
506     * Returns whether the preference node with the given path name exists. The
507     * path is treated as relative to this node if it doesn't start with a slash,
508     * otherwise it is treated as an absolute path.
509     * <p>
510     * Please note that if this node has been removed, an invocation of this
511     * node will throw an {@code IllegalStateException} unless the given path is
512     * an empty string, which will return {@code false}.
513     * </p>
514     *
515     * @param path
516     *            the path name of the preference node to query.
517     * @return {@code true}, if the queried preference node exists, {@code false}
518     *         otherwise.
519     * @throws IllegalStateException
520     *             if this node has been removed and the path is not an empty
521     *             string.
522     * @throws IllegalArgumentException
523     *             if the path name is invalid.
524     * @throws NullPointerException
525     *             if the given path is {@code null}.
526     * @throws BackingStoreException
527     *             if the backing store is unavailable or causes an operation
528     *             failure.
529     */
530    public abstract boolean nodeExists(String path) throws BackingStoreException;
531
532    /**
533     * Returns the parent preference node of this node or {@code null} if this
534     * node is the root node.
535     *
536     * @return the parent preference node of this node.
537     * @throws IllegalStateException
538     *             if this node has been removed.
539     */
540    public abstract Preferences parent();
541
542    /**
543     * Adds a new preference to this node using the given key and value or
544     * updates the value if a preference with the given key already exists.
545     *
546     * @param key
547     *            the preference key to be added or updated.
548     * @param value
549     *            the preference value for the given key.
550     * @throws NullPointerException
551     *             if the given key or value is {@code null}.
552     * @throws IllegalArgumentException
553     *             if the given key's length is bigger than {@code
554     *             MAX_KEY_LENGTH} or the value's length is bigger than {@code
555     *             MAX_VALUE_LENGTH}.
556     * @throws IllegalStateException
557     *             if this node has been removed.
558     */
559    public abstract void put(String key, String value);
560
561    /**
562     * Adds a new preference with a {@code boolean} value to this node using the
563     * given key and value or updates the value if a preference with the given
564     * key already exists.
565     *
566     * @param key
567     *            the preference key to be added or updated.
568     * @param value
569     *            the preference {@code boolean} value for the given key.
570     * @throws NullPointerException
571     *             if the given key is {@code null}.
572     * @throws IllegalArgumentException
573     *             if the given key's length is bigger than {@code
574     *             MAX_KEY_LENGTH}.
575     * @throws IllegalStateException
576     *             if this node has been removed.
577     */
578    public abstract void putBoolean(String key, boolean value);
579
580    /**
581     * Adds a new preference to this node using the given key and the string
582     * form of the given value or updates the value if a preference with the
583     * given key already exists.
584     * <p>
585     * The string form of the value is the Base64-encoded binary data of the
586     * given byte array. The Base64 encoding is as defined in <a
587     * href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.
588     * </p>
589     *
590     * @param key
591     *            the preference key to be added or updated.
592     * @param value
593     *            the preference value for the given key.
594     * @throws NullPointerException
595     *             if the given key or value is {@code null}.
596     * @throws IllegalArgumentException
597     *             if the given key's length is bigger than {@code
598     *             MAX_KEY_LENGTH} or value's length is bigger than three
599     *             quarters of {@code MAX_KEY_LENGTH}.
600     * @throws IllegalStateException
601     *             if this node has been removed.
602     */
603    public abstract void putByteArray(String key, byte[] value);
604
605    /**
606     * Adds a new preference to this node using the given key and {@code double}
607     * value or updates the value if a preference with the
608     * given key already exists.
609     * <p>
610     * The value is stored in its string form, which is the result of invoking
611     * {@link Double#toString(double) Double.toString(double)}.
612     * </p>
613     *
614     * @param key
615     *            the preference key to be added or updated.
616     * @param value
617     *            the preference value for the given key.
618     * @throws NullPointerException
619     *             if the given key is {@code null}.
620     * @throws IllegalArgumentException
621     *             if the given key's length is bigger than {@code
622     *             MAX_KEY_LENGTH}.
623     * @throws IllegalStateException
624     *             if this node has been removed.
625     */
626    public abstract void putDouble(String key, double value);
627
628    /**
629     * Adds a new preference to this node using the given key and {@code float}
630     * value or updates the value if a preference with the
631     * given key already exists.
632     * <p>
633     * The value is stored in its string form, which is the result of invoking
634     * {@link Float#toString(float) Float.toString(float)}.
635     * </p>
636     *
637     * @param key
638     *            the preference key to be added or updated.
639     * @param value
640     *            the preference value for the given key.
641     * @throws NullPointerException
642     *             if the given key is {@code null}.
643     * @throws IllegalArgumentException
644     *             if the given key's length is bigger than {@code
645     *             MAX_KEY_LENGTH}.
646     * @throws IllegalStateException
647     *             if this node has been removed.
648     */
649    public abstract void putFloat(String key, float value);
650
651    /**
652     * Adds a new preference to this node using the given key and {@code int}
653     * value or updates the value if a preference with the
654     * given key already exists.
655     * <p>
656     * The value is stored in its string form, which is the result of invoking
657     * {@link Integer#toString(int) Integer.toString(int)}.
658     * </p>
659     *
660     * @param key
661     *            the preference key to be added or updated.
662     * @param value
663     *            the preference value for the given key.
664     * @throws NullPointerException
665     *             if the given key is {@code null}.
666     * @throws IllegalArgumentException
667     *             if the given key's length is bigger than {@code
668     *             MAX_KEY_LENGTH}.
669     * @throws IllegalStateException
670     *             if this node has been removed.
671     */
672    public abstract void putInt(String key, int value);
673
674    /**
675     * Adds a new preference to this node using the given key and {@code long}
676     * value or updates the value if a preference with the
677     * given key already exists.
678     * <p>
679     * The value is stored in its string form, which is the result of invoking
680     * {@link Long#toString(long) Long.toString(long)}.
681     * </p>
682     *
683     * @param key
684     *            the preference key to be added or updated.
685     * @param value
686     *            the preference value for the given key.
687     * @throws NullPointerException
688     *             if the given key is {@code null}.
689     * @throws IllegalArgumentException
690     *             if the given key's length is bigger than {@code
691     *             MAX_KEY_LENGTH}.
692     * @throws IllegalStateException
693     *             if this node has been removed.
694     */
695    public abstract void putLong(String key, long value);
696
697    /**
698     * Removes the preference mapped to the given key from this node.
699     *
700     * @param key
701     *            the key of the preference to be removed.
702     * @throws NullPointerException
703     *             if the given key is {@code null}.
704     * @throws IllegalStateException
705     *             if this node has been removed.
706     */
707    public abstract void remove(String key);
708
709    /**
710     * Removes this preference node with all its descendants. The removal won't
711     * necessarily be persisted until the method {@code flush()} is invoked.
712     *
713     * @throws BackingStoreException
714     *             if the backing store is unavailable or causes an operation
715     *             failure.
716     * @throws IllegalStateException
717     *             if this node has been removed.
718     * @throws UnsupportedOperationException
719     *             if this is a root node.
720     */
721    public abstract void removeNode() throws BackingStoreException;
722
723    /**
724     * Registers a {@code NodeChangeListener} instance for this node, which will
725     * handle {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired
726     * when a child node has been added to or removed from this node.
727     *
728     * @param ncl
729     *            the listener to be registered.
730     * @throws NullPointerException
731     *             if the given listener is {@code null}.
732     * @throws IllegalStateException
733     *             if this node has been removed.
734     */
735    public abstract void addNodeChangeListener(NodeChangeListener ncl);
736
737    /**
738     * Registers a {@code PreferenceChangeListener} instance for this node,
739     * which will handle {@code PreferenceChangeEvent}s. {@code
740     * PreferenceChangeEvent}s will be fired when a preference has been added
741     * to, removed from, or updated for this node.
742     *
743     * @param pcl
744     *            the listener to be registered.
745     * @throws NullPointerException
746     *             if the given listener is {@code null}.
747     * @throws IllegalStateException
748     *             if this node has been removed.
749     */
750    public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl);
751
752    /**
753     * Removes the given {@code NodeChangeListener} instance from this node.
754     *
755     * @param ncl
756     *            the listener to be removed.
757     * @throws IllegalArgumentException
758     *             if the given listener is {@code null}.
759     * @throws IllegalStateException
760     *             if this node has been removed.
761     */
762    public abstract void removeNodeChangeListener (NodeChangeListener ncl);
763
764    /**
765     * Removes the given {@code PreferenceChangeListener} instance from this
766     * node.
767     *
768     * @param pcl
769     *            the listener to be removed.
770     * @throws IllegalArgumentException
771     *             if the given listener is {@code null}.
772     * @throws IllegalStateException
773     *             if this node has been removed.
774     */
775    public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl);
776
777    /**
778     * Synchronizes the data of this preference node and its descendants with
779     * the back-end preference store. Any changes found in the back-end data
780     * should be reflected in this node and its descendants, and at the same
781     * time any local changes to this node and descendants should be persisted.
782     *
783     * @throws BackingStoreException
784     *             if the backing store is unavailable or causes an operation
785     *             failure.
786     * @throws IllegalStateException
787     *             if this node has been removed.
788     */
789    public abstract void sync() throws BackingStoreException;
790
791    /**
792     * <strong>Legacy code; do not use.</strong> On Android, the Preference nodes
793     * corresponding to the "system" and "user" preferences are stored in sections
794     * of the file system that are inaccessible to apps. Further, allowing apps to set
795     * "system wide" preferences is contrary to android's security model.
796     *
797     * Returns the system preference node for the package of the given class.
798     * The absolute path of the returned node is one slash followed by the given
799     * class's full package name, replacing each period character ('.') with
800     * a slash. For example, the absolute path of the preference associated with
801     * the class Object would be "/java/lang". As a special case, the unnamed
802     * package is associated with a preference node "/&lt;unnamed&gt;". This
803     * method will create the node and its ancestors as needed. Any nodes created
804     * by this method won't necessarily be persisted until the method {@code
805     * flush()} is invoked.
806     *
807     * @param c
808     *            the given class.
809     * @return the system preference node for the package of the given class.
810     * @throws NullPointerException
811     *             if the given class is {@code null}.
812     */
813    public static Preferences systemNodeForPackage(Class<?> c) {
814        return factory.systemRoot().node(getNodeName(c));
815    }
816
817    /**
818     * <strong>Legacy code; do not use.</strong> On Android, the Preference nodes
819     * corresponding to the "system" and "user" preferences are stored in sections
820     * of the file system that are inaccessible to apps. Further, allowing apps to set
821     * "system wide" preferences is contrary to android's security model.
822     *
823     * Returns the root node of the system preference hierarchy.
824     *
825     * @return the system preference hierarchy root node.
826     */
827    public static Preferences systemRoot() {
828        return factory.systemRoot();
829    }
830
831    /**
832     *
833     * <strong>Legacy code; do not use.</strong> On Android, the Preference nodes
834     * corresponding to the "system" and "user" preferences are stored in sections
835     * of the file system that are inaccessible to apps. Further, allowing apps to set
836     * "system wide" preferences is contrary to android's security model.
837     *
838     * <p>
839     * Returns the user preference node for the package of the given class.
840     * The absolute path of the returned node is one slash followed by the given
841     * class's full package name, replacing each period character ('.') with
842     * a slash. For example, the absolute path of the preference associated with
843     * the class Object would be "/java/lang". As a special case, the unnamed
844     * package is associated with a preference node "/&lt;unnamed&gt;". This
845     * method will create the node and its ancestors as needed. Any nodes created
846     * by this method won't necessarily be persisted until the method {@code
847     * flush()} is invoked.
848     *
849     * @return the user preference node for the package of the given class.
850     * @throws NullPointerException
851     *             if the given class is {@code null}.
852     */
853    public static Preferences userNodeForPackage(Class<?> c) {
854        return factory.userRoot().node(getNodeName(c));
855    }
856
857    //parse node's absolute path from class instance
858    private static String getNodeName(Class<?> c){
859        Package p = c.getPackage();
860        if (p == null){
861            return "/<unnamed>";
862        }
863        return "/"+p.getName().replace('.', '/');
864    }
865
866    /**
867     * <strong>Legacy code; do not use.</strong> On Android, the Preference nodes
868     * corresponding to the "system" and "user" preferences are stored in sections
869     * of the file system that are inaccessible to apps. Further, allowing apps to set
870     * "system wide" preferences is contrary to android's security model.
871     *
872     * Returns the root node of the user preference hierarchy.
873     *
874     * @return the user preference hierarchy root node.
875     */
876    public static Preferences userRoot() {
877        return factory.userRoot();
878    }
879
880    /**
881     * Returns a string representation of this node. The format is "User/System
882     * Preference Node: " followed by this node's absolute path.
883     *
884     * @return the string representation of this node.
885     */
886    @Override
887    public abstract String toString();
888}
889