Path: blob/master/src/java.prefs/share/classes/java/util/prefs/Preferences.java
41159 views
/*1* Copyright (c) 2000, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.util.prefs;2627import java.io.InputStream;28import java.io.IOException;29import java.io.OutputStream;30import java.security.AccessController;31import java.security.Permission;32import java.security.PrivilegedAction;33import java.util.Iterator;34import java.util.ServiceLoader;35import java.util.ServiceConfigurationError;3637// These imports needed only as a workaround for a JavaDoc bug38import java.lang.RuntimePermission;39import java.lang.Integer;40import java.lang.Long;41import java.lang.Float;42import java.lang.Double;4344/**45* A node in a hierarchical collection of preference data. This class46* allows applications to store and retrieve user and system47* preference and configuration data. This data is stored48* persistently in an implementation-dependent backing store. Typical49* implementations include flat files, OS-specific registries,50* directory servers and SQL databases. The user of this class needn't51* be concerned with details of the backing store.52*53* <p>There are two separate trees of preference nodes, one for user54* preferences and one for system preferences. Each user has a separate user55* preference tree, and all users in a given system share the same system56* preference tree. The precise description of "user" and "system" will vary57* from implementation to implementation. Typical information stored in the58* user preference tree might include font choice, color choice, or preferred59* window location and size for a particular application. Typical information60* stored in the system preference tree might include installation61* configuration data for an application.62*63* <p>Nodes in a preference tree are named in a similar fashion to64* directories in a hierarchical file system. Every node in a preference65* tree has a <i>node name</i> (which is not necessarily unique),66* a unique <i>absolute path name</i>, and a path name <i>relative</i> to each67* ancestor including itself.68*69* <p>The root node has a node name of the empty string (""). Every other70* node has an arbitrary node name, specified at the time it is created. The71* only restrictions on this name are that it cannot be the empty string, and72* it cannot contain the slash character ('/').73*74* <p>The root node has an absolute path name of {@code "/"}. Children of75* the root node have absolute path names of {@code "/" + }<i><node76* name></i>. All other nodes have absolute path names of <i><parent's77* absolute path name></i>{@code + "/" + }<i><node name></i>.78* Note that all absolute path names begin with the slash character.79*80* <p>A node <i>n</i>'s path name relative to its ancestor <i>a</i>81* is simply the string that must be appended to <i>a</i>'s absolute path name82* in order to form <i>n</i>'s absolute path name, with the initial slash83* character (if present) removed. Note that:84* <ul>85* <li>No relative path names begin with the slash character.86* <li>Every node's path name relative to itself is the empty string.87* <li>Every node's path name relative to its parent is its node name (except88* for the root node, which does not have a parent).89* <li>Every node's path name relative to the root is its absolute path name90* with the initial slash character removed.91* </ul>92*93* <p>Note finally that:94* <ul>95* <li>No path name contains multiple consecutive slash characters.96* <li>No path name with the exception of the root's absolute path name97* ends in the slash character.98* <li>Any string that conforms to these two rules is a valid path name.99* </ul>100*101* <p>All of the methods that modify preferences data are permitted to operate102* asynchronously; they may return immediately, and changes will eventually103* propagate to the persistent backing store with an implementation-dependent104* delay. The {@code flush} method may be used to synchronously force105* updates to the backing store. Normal termination of the Java Virtual106* Machine will <i>not</i> result in the loss of pending updates -- an explicit107* {@code flush} invocation is <i>not</i> required upon termination to ensure108* that pending updates are made persistent.109*110* <p>All of the methods that read preferences from a {@code Preferences}111* object require the invoker to provide a default value. The default value is112* returned if no value has been previously set <i>or if the backing store is113* unavailable</i>. The intent is to allow applications to operate, albeit114* with slightly degraded functionality, even if the backing store becomes115* unavailable. Several methods, like {@code flush}, have semantics that116* prevent them from operating if the backing store is unavailable. Ordinary117* applications should have no need to invoke any of these methods, which can118* be identified by the fact that they are declared to throw {@link119* BackingStoreException}.120*121* <p>The methods in this class may be invoked concurrently by multiple threads122* in a single JVM without the need for external synchronization, and the123* results will be equivalent to some serial execution. If this class is used124* concurrently <i>by multiple JVMs</i> that store their preference data in125* the same backing store, the data store will not be corrupted, but no126* other guarantees are made concerning the consistency of the preference127* data.128*129* <p>This class contains an export/import facility, allowing preferences130* to be "exported" to an XML document, and XML documents representing131* preferences to be "imported" back into the system. This facility132* may be used to back up all or part of a preference tree, and133* subsequently restore from the backup.134*135* <p>The XML document has the following DOCTYPE declaration:136* <pre>{@code137* <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">138* }</pre>139* Note that the system URI (http://java.sun.com/dtd/preferences.dtd) is140* <i>not</i> accessed when exporting or importing preferences; it merely141* serves as a string to uniquely identify the DTD, which is:142* <pre>{@code143* <?xml version="1.0" encoding="UTF-8"?>144*145* <!-- DTD for a Preferences tree. -->146*147* <!-- The preferences element is at the root of an XML document148* representing a Preferences tree. -->149* <!ELEMENT preferences (root)>150*151* <!-- The preferences element contains an optional version attribute,152* which specifies version of DTD. -->153* <!ATTLIST preferences EXTERNAL_XML_VERSION CDATA "0.0" >154*155* <!-- The root element has a map representing the root's preferences156* (if any), and one node for each child of the root (if any). -->157* <!ELEMENT root (map, node*) >158*159* <!-- Additionally, the root contains a type attribute, which160* specifies whether it's the system or user root. -->161* <!ATTLIST root162* type (system|user) #REQUIRED >163*164* <!-- Each node has a map representing its preferences (if any),165* and one node for each child (if any). -->166* <!ELEMENT node (map, node*) >167*168* <!-- Additionally, each node has a name attribute -->169* <!ATTLIST node170* name CDATA #REQUIRED >171*172* <!-- A map represents the preferences stored at a node (if any). -->173* <!ELEMENT map (entry*) >174*175* <!-- An entry represents a single preference, which is simply176* a key-value pair. -->177* <!ELEMENT entry EMPTY >178* <!ATTLIST entry179* key CDATA #REQUIRED180* value CDATA #REQUIRED >181* }</pre>182*183* Every {@code Preferences} implementation must have an associated {@link184* PreferencesFactory} implementation. Every Java(TM) SE implementation must provide185* some means of specifying which {@code PreferencesFactory} implementation186* is used to generate the root preferences nodes. This allows the187* administrator to replace the default preferences implementation with an188* alternative implementation.189*190* @implNote191* The {@code PreferencesFactory} implementation is located as follows:192*193* <ol>194*195* <li><p>If the system property196* {@systemProperty java.util.prefs.PreferencesFactory} is defined, then it is197* taken to be the fully-qualified name of a class implementing the198* {@code PreferencesFactory} interface. The class is loaded and199* instantiated; if this process fails then an unspecified error is200* thrown.</p></li>201*202* <li><p> If a {@code PreferencesFactory} implementation class file203* has been installed in a jar file that is visible to the204* {@link java.lang.ClassLoader#getSystemClassLoader system class loader},205* and that jar file contains a provider-configuration file named206* {@code java.util.prefs.PreferencesFactory} in the resource207* directory {@code META-INF/services}, then the first class name208* specified in that file is taken. If more than one such jar file is209* provided, the first one found will be used. The class is loaded210* and instantiated; if this process fails then an unspecified error211* is thrown. </p></li>212*213* <li><p>Finally, if neither the above-mentioned system property nor214* an extension jar file is provided, then the system-wide default215* {@code PreferencesFactory} implementation for the underlying216* platform is loaded and instantiated.</p></li>217*218* </ol>219*220* @author Josh Bloch221* @since 1.4222*/223public abstract class Preferences {224225private static final PreferencesFactory factory = factory();226227@SuppressWarnings("removal")228private static PreferencesFactory factory() {229// 1. Try user-specified system property230String factoryName = AccessController.doPrivileged(231new PrivilegedAction<String>() {232public String run() {233return System.getProperty(234"java.util.prefs.PreferencesFactory");}});235if (factoryName != null) {236// FIXME: This code should be run in a doPrivileged and237// not use the context classloader, to avoid being238// dependent on the invoking thread.239// Checking AllPermission also seems wrong.240try {241@SuppressWarnings("deprecation")242Object result =Class.forName(factoryName, false,243ClassLoader.getSystemClassLoader())244.newInstance();245return (PreferencesFactory)result;246} catch (Exception ex) {247try {248// workaround for javaws, plugin,249// load factory class using non-system classloader250SecurityManager sm = System.getSecurityManager();251if (sm != null) {252sm.checkPermission(new java.security.AllPermission());253}254@SuppressWarnings("deprecation")255Object result = Class.forName(factoryName, false,256Thread.currentThread()257.getContextClassLoader())258.newInstance();259return (PreferencesFactory) result;260} catch (Exception e) {261throw new InternalError(262"Can't instantiate Preferences factory "263+ factoryName, e);264}265}266}267268return AccessController.doPrivileged(269new PrivilegedAction<PreferencesFactory>() {270public PreferencesFactory run() {271return factory1();}});272}273274private static PreferencesFactory factory1() {275// 2. Try service provider interface276Iterator<PreferencesFactory> itr = ServiceLoader277.load(PreferencesFactory.class, ClassLoader.getSystemClassLoader())278.iterator();279280// choose first provider instance281while (itr.hasNext()) {282try {283return itr.next();284} catch (ServiceConfigurationError sce) {285if (sce.getCause() instanceof SecurityException) {286// Ignore the security exception, try the next provider287continue;288}289throw sce;290}291}292293// 3. Use platform-specific system-wide default294String osName = System.getProperty("os.name");295String platformFactory;296if (osName.startsWith("Windows")) {297platformFactory = "java.util.prefs.WindowsPreferencesFactory";298} else if (osName.contains("OS X")) {299platformFactory = "java.util.prefs.MacOSXPreferencesFactory";300} else {301platformFactory = "java.util.prefs.FileSystemPreferencesFactory";302}303try {304@SuppressWarnings("deprecation")305Object result = Class.forName(platformFactory, false,306Preferences.class.getClassLoader()).newInstance();307return (PreferencesFactory) result;308} catch (Exception e) {309throw new InternalError(310"Can't instantiate platform default Preferences factory "311+ platformFactory, e);312}313}314315/**316* Maximum length of string allowed as a key (80 characters).317*/318public static final int MAX_KEY_LENGTH = 80;319320/**321* Maximum length of string allowed as a value (8192 characters).322*/323public static final int MAX_VALUE_LENGTH = 8*1024;324325/**326* Maximum length of a node name (80 characters).327*/328public static final int MAX_NAME_LENGTH = 80;329330/**331* Returns the preference node from the calling user's preference tree332* that is associated (by convention) with the specified class's package.333* The convention is as follows: the absolute path name of the node is the334* fully qualified package name, preceded by a slash ({@code '/'}), and335* with each period ({@code '.'}) replaced by a slash. For example the336* absolute path name of the node associated with the class337* {@code com.acme.widget.Foo} is {@code /com/acme/widget}.338*339* <p>This convention does not apply to the unnamed package, whose340* associated preference node is {@code <unnamed>}. This node341* is not intended for long term use, but for convenience in the early342* development of programs that do not yet belong to a package, and343* for "throwaway" programs. <i>Valuable data should not be stored344* at this node as it is shared by all programs that use it.</i>345*346* <p>A class {@code Foo} wishing to access preferences pertaining to its347* package can obtain a preference node as follows: <pre>348* static Preferences prefs = Preferences.userNodeForPackage(Foo.class);349* </pre>350* This idiom obviates the need for using a string to describe the351* preferences node and decreases the likelihood of a run-time failure.352* (If the class name is misspelled, it will typically result in a353* compile-time error.)354*355* <p>Invoking this method will result in the creation of the returned356* node and its ancestors if they do not already exist. If the returned357* node did not exist prior to this call, this node and any ancestors that358* were created by this call are not guaranteed to become permanent until359* the {@code flush} method is called on the returned node (or one of its360* ancestors or descendants).361*362* @param c the class for whose package a user preference node is desired.363* @return the user preference node associated with the package of which364* {@code c} is a member.365* @throws NullPointerException if {@code c} is {@code null}.366* @throws SecurityException if a security manager is present and367* it denies {@code RuntimePermission("preferences")}.368* @see RuntimePermission369*/370public static Preferences userNodeForPackage(Class<?> c) {371return userRoot().node(nodeName(c));372}373374/**375* Returns the preference node from the system preference tree that is376* associated (by convention) with the specified class's package. The377* convention is as follows: the absolute path name of the node is the378* fully qualified package name, preceded by a slash ({@code '/'}), and379* with each period ({@code '.'}) replaced by a slash. For example the380* absolute path name of the node associated with the class381* {@code com.acme.widget.Foo} is {@code /com/acme/widget}.382*383* <p>This convention does not apply to the unnamed package, whose384* associated preference node is {@code <unnamed>}. This node385* is not intended for long term use, but for convenience in the early386* development of programs that do not yet belong to a package, and387* for "throwaway" programs. <i>Valuable data should not be stored388* at this node as it is shared by all programs that use it.</i>389*390* <p>A class {@code Foo} wishing to access preferences pertaining to its391* package can obtain a preference node as follows: <pre>392* static Preferences prefs = Preferences.systemNodeForPackage(Foo.class);393* </pre>394* This idiom obviates the need for using a string to describe the395* preferences node and decreases the likelihood of a run-time failure.396* (If the class name is misspelled, it will typically result in a397* compile-time error.)398*399* <p>Invoking this method will result in the creation of the returned400* node and its ancestors if they do not already exist. If the returned401* node did not exist prior to this call, this node and any ancestors that402* were created by this call are not guaranteed to become permanent until403* the {@code flush} method is called on the returned node (or one of its404* ancestors or descendants).405*406* @param c the class for whose package a system preference node is desired.407* @return the system preference node associated with the package of which408* {@code c} is a member.409* @throws NullPointerException if {@code c} is {@code null}.410* @throws SecurityException if a security manager is present and411* it denies {@code RuntimePermission("preferences")}.412* @see RuntimePermission413*/414public static Preferences systemNodeForPackage(Class<?> c) {415return systemRoot().node(nodeName(c));416}417418/**419* Returns the absolute path name of the node corresponding to the package420* of the specified object.421*422* @throws IllegalArgumentException if the package has node preferences423* node associated with it.424*/425private static String nodeName(Class<?> c) {426if (c.isArray())427throw new IllegalArgumentException(428"Arrays have no associated preferences node.");429String className = c.getName();430int pkgEndIndex = className.lastIndexOf('.');431if (pkgEndIndex < 0)432return "/<unnamed>";433String packageName = className.substring(0, pkgEndIndex);434return "/" + packageName.replace('.', '/');435}436437/**438* This permission object represents the permission required to get439* access to the user or system root (which in turn allows for all440* other operations).441*/442private static Permission prefsPerm = new RuntimePermission("preferences");443444/**445* Returns the root preference node for the calling user.446*447* @return the root preference node for the calling user.448* @throws SecurityException If a security manager is present and449* it denies {@code RuntimePermission("preferences")}.450* @see RuntimePermission451*/452public static Preferences userRoot() {453@SuppressWarnings("removal")454SecurityManager security = System.getSecurityManager();455if (security != null)456security.checkPermission(prefsPerm);457458return factory.userRoot();459}460461/**462* Returns the root preference node for the system.463*464* @return the root preference node for the system.465* @throws SecurityException If a security manager is present and466* it denies {@code RuntimePermission("preferences")}.467* @see RuntimePermission468*/469public static Preferences systemRoot() {470@SuppressWarnings("removal")471SecurityManager security = System.getSecurityManager();472if (security != null)473security.checkPermission(prefsPerm);474475return factory.systemRoot();476}477478/**479* Sole constructor. (For invocation by subclass constructors, typically480* implicit.)481*/482protected Preferences() {483}484485/**486* Associates the specified value with the specified key in this487* preference node.488*489* @param key key with which the specified value is to be associated.490* @param value value to be associated with the specified key.491* @throws NullPointerException if key or value is {@code null}.492* @throws IllegalArgumentException if {@code key.length()} exceeds493* {@code MAX_KEY_LENGTH} or if {@code value.length} exceeds494* {@code MAX_VALUE_LENGTH}.495* @throws IllegalStateException if this node (or an ancestor) has been496* removed with the {@link #removeNode()} method.497* @throws IllegalArgumentException if either key or value contain498* the null control character, code point U+0000.499*/500public abstract void put(String key, String value);501502/**503* Returns the value associated with the specified key in this preference504* node. Returns the specified default if there is no value associated505* with the key, or the backing store is inaccessible.506*507* <p>Some implementations may store default values in their backing508* stores. If there is no value associated with the specified key509* but there is such a <i>stored default</i>, it is returned in510* preference to the specified default.511*512* @param key key whose associated value is to be returned.513* @param def the value to be returned in the event that this514* preference node has no value associated with {@code key}.515* @return the value associated with {@code key}, or {@code def}516* if no value is associated with {@code key}, or the backing517* store is inaccessible.518* @throws IllegalStateException if this node (or an ancestor) has been519* removed with the {@link #removeNode()} method.520* @throws NullPointerException if {@code key} is {@code null}. (A521* {@code null} value for {@code def} <i>is</i> permitted.)522* @throws IllegalArgumentException if key contains the null control523* character, code point U+0000.524*/525public abstract String get(String key, String def);526527/**528* Removes the value associated with the specified key in this preference529* node, if any.530*531* <p>If this implementation supports <i>stored defaults</i>, and there is532* such a default for the specified preference, the stored default will be533* "exposed" by this call, in the sense that it will be returned534* by a succeeding call to {@code get}.535*536* @param key key whose mapping is to be removed from the preference node.537* @throws NullPointerException if {@code key} is {@code null}.538* @throws IllegalStateException if this node (or an ancestor) has been539* removed with the {@link #removeNode()} method.540* @throws IllegalArgumentException if key contains the null control541* character, code point U+0000.542*/543public abstract void remove(String key);544545/**546* Removes all of the preferences (key-value associations) in this547* preference node. This call has no effect on any descendants548* of this node.549*550* <p>If this implementation supports <i>stored defaults</i>, and this551* node in the preferences hierarchy contains any such defaults,552* the stored defaults will be "exposed" by this call, in the sense that553* they will be returned by succeeding calls to {@code get}.554*555* @throws BackingStoreException if this operation cannot be completed556* due to a failure in the backing store, or inability to557* communicate with it.558* @throws IllegalStateException if this node (or an ancestor) has been559* removed with the {@link #removeNode()} method.560* @see #removeNode()561*/562public abstract void clear() throws BackingStoreException;563564/**565* Associates a string representing the specified int value with the566* specified key in this preference node. The associated string is the567* one that would be returned if the int value were passed to568* {@link Integer#toString(int)}. This method is intended for use in569* conjunction with {@link #getInt}.570*571* @param key key with which the string form of value is to be associated.572* @param value value whose string form is to be associated with key.573* @throws NullPointerException if {@code key} is {@code null}.574* @throws IllegalArgumentException if {@code key.length()} exceeds575* {@code MAX_KEY_LENGTH}.576* @throws IllegalStateException if this node (or an ancestor) has been577* removed with the {@link #removeNode()} method.578* @throws IllegalArgumentException if key contains579* the null control character, code point U+0000.580* @see #getInt(String,int)581*/582public abstract void putInt(String key, int value);583584/**585* Returns the int value represented by the string associated with the586* specified key in this preference node. The string is converted to587* an integer as by {@link Integer#parseInt(String)}. Returns the588* specified default if there is no value associated with the key,589* the backing store is inaccessible, or if590* {@code Integer.parseInt(String)} would throw a {@link591* NumberFormatException} if the associated value were passed. This592* method is intended for use in conjunction with {@link #putInt}.593*594* <p>If the implementation supports <i>stored defaults</i> and such a595* default exists, is accessible, and could be converted to an int596* with {@code Integer.parseInt}, this int is returned in preference to597* the specified default.598*599* @param key key whose associated value is to be returned as an int.600* @param def the value to be returned in the event that this601* preference node has no value associated with {@code key}602* or the associated value cannot be interpreted as an int,603* or the backing store is inaccessible.604* @return the int value represented by the string associated with605* {@code key} in this preference node, or {@code def} if the606* associated value does not exist or cannot be interpreted as607* an int.608* @throws IllegalStateException if this node (or an ancestor) has been609* removed with the {@link #removeNode()} method.610* @throws NullPointerException if {@code key} is {@code null}.611* @throws IllegalArgumentException if key contains the null control612* character, code point U+0000.613* @see #putInt(String,int)614* @see #get(String,String)615*/616public abstract int getInt(String key, int def);617618/**619* Associates a string representing the specified long value with the620* specified key in this preference node. The associated string is the621* one that would be returned if the long value were passed to622* {@link Long#toString(long)}. This method is intended for use in623* conjunction with {@link #getLong}.624*625* @param key key with which the string form of value is to be associated.626* @param value value whose string form is to be associated with key.627* @throws NullPointerException if {@code key} is {@code null}.628* @throws IllegalArgumentException if {@code key.length()} exceeds629* {@code MAX_KEY_LENGTH}.630* @throws IllegalStateException if this node (or an ancestor) has been631* removed with the {@link #removeNode()} method.632* @throws IllegalArgumentException if key contains633* the null control character, code point U+0000.634* @see #getLong(String,long)635*/636public abstract void putLong(String key, long value);637638/**639* Returns the long value represented by the string associated with the640* specified key in this preference node. The string is converted to641* a long as by {@link Long#parseLong(String)}. Returns the642* specified default if there is no value associated with the key,643* the backing store is inaccessible, or if644* {@code Long.parseLong(String)} would throw a {@link645* NumberFormatException} if the associated value were passed. This646* method is intended for use in conjunction with {@link #putLong}.647*648* <p>If the implementation supports <i>stored defaults</i> and such a649* default exists, is accessible, and could be converted to a long650* with {@code Long.parseLong}, this long is returned in preference to651* the specified default.652*653* @param key key whose associated value is to be returned as a long.654* @param def the value to be returned in the event that this655* preference node has no value associated with {@code key}656* or the associated value cannot be interpreted as a long,657* or the backing store is inaccessible.658* @return the long value represented by the string associated with659* {@code key} in this preference node, or {@code def} if the660* associated value does not exist or cannot be interpreted as661* a long.662* @throws IllegalStateException if this node (or an ancestor) has been663* removed with the {@link #removeNode()} method.664* @throws NullPointerException if {@code key} is {@code null}.665* @throws IllegalArgumentException if key contains the null control666* character, code point U+0000.667* @see #putLong(String,long)668* @see #get(String,String)669*/670public abstract long getLong(String key, long def);671672/**673* Associates a string representing the specified boolean value with the674* specified key in this preference node. The associated string is675* {@code "true"} if the value is true, and {@code "false"} if it is676* false. This method is intended for use in conjunction with677* {@link #getBoolean}.678*679* @param key key with which the string form of value is to be associated.680* @param value value whose string form is to be associated with key.681* @throws NullPointerException if {@code key} is {@code null}.682* @throws IllegalArgumentException if {@code key.length()} exceeds683* {@code MAX_KEY_LENGTH}.684* @throws IllegalStateException if this node (or an ancestor) has been685* removed with the {@link #removeNode()} method.686* @throws IllegalArgumentException if key contains687* the null control character, code point U+0000.688* @see #getBoolean(String,boolean)689* @see #get(String,String)690*/691public abstract void putBoolean(String key, boolean value);692693/**694* Returns the boolean value represented by the string associated with the695* specified key in this preference node. Valid strings696* are {@code "true"}, which represents true, and {@code "false"}, which697* represents false. Case is ignored, so, for example, {@code "TRUE"}698* and {@code "False"} are also valid. This method is intended for use in699* conjunction with {@link #putBoolean}.700*701* <p>Returns the specified default if there is no value702* associated with the key, the backing store is inaccessible, or if the703* associated value is something other than {@code "true"} or704* {@code "false"}, ignoring case.705*706* <p>If the implementation supports <i>stored defaults</i> and such a707* default exists and is accessible, it is used in preference to the708* specified default, unless the stored default is something other than709* {@code "true"} or {@code "false"}, ignoring case, in which case the710* specified default is used.711*712* @param key key whose associated value is to be returned as a boolean.713* @param def the value to be returned in the event that this714* preference node has no value associated with {@code key}715* or the associated value cannot be interpreted as a boolean,716* or the backing store is inaccessible.717* @return the boolean value represented by the string associated with718* {@code key} in this preference node, or {@code def} if the719* associated value does not exist or cannot be interpreted as720* a boolean.721* @throws IllegalStateException if this node (or an ancestor) has been722* removed with the {@link #removeNode()} method.723* @throws NullPointerException if {@code key} is {@code null}.724* @throws IllegalArgumentException if key contains the null control725* character, code point U+0000.726* @see #get(String,String)727* @see #putBoolean(String,boolean)728*/729public abstract boolean getBoolean(String key, boolean def);730731/**732* Associates a string representing the specified float value with the733* specified key in this preference node. The associated string is the734* one that would be returned if the float value were passed to735* {@link Float#toString(float)}. This method is intended for use in736* conjunction with {@link #getFloat}.737*738* @param key key with which the string form of value is to be associated.739* @param value value whose string form is to be associated with key.740* @throws NullPointerException if {@code key} is {@code null}.741* @throws IllegalArgumentException if {@code key.length()} exceeds742* {@code MAX_KEY_LENGTH}.743* @throws IllegalStateException if this node (or an ancestor) has been744* removed with the {@link #removeNode()} method.745* @throws IllegalArgumentException if key contains746* the null control character, code point U+0000.747* @see #getFloat(String,float)748*/749public abstract void putFloat(String key, float value);750751/**752* Returns the float value represented by the string associated with the753* specified key in this preference node. The string is converted to an754* integer as by {@link Float#parseFloat(String)}. Returns the specified755* default if there is no value associated with the key, the backing store756* is inaccessible, or if {@code Float.parseFloat(String)} would throw a757* {@link NumberFormatException} if the associated value were passed.758* This method is intended for use in conjunction with {@link #putFloat}.759*760* <p>If the implementation supports <i>stored defaults</i> and such a761* default exists, is accessible, and could be converted to a float762* with {@code Float.parseFloat}, this float is returned in preference to763* the specified default.764*765* @param key key whose associated value is to be returned as a float.766* @param def the value to be returned in the event that this767* preference node has no value associated with {@code key}768* or the associated value cannot be interpreted as a float,769* or the backing store is inaccessible.770* @return the float value represented by the string associated with771* {@code key} in this preference node, or {@code def} if the772* associated value does not exist or cannot be interpreted as773* a float.774* @throws IllegalStateException if this node (or an ancestor) has been775* removed with the {@link #removeNode()} method.776* @throws NullPointerException if {@code key} is {@code null}.777* @throws IllegalArgumentException if key contains the null control778* character, code point U+0000.779* @see #putFloat(String,float)780* @see #get(String,String)781*/782public abstract float getFloat(String key, float def);783784/**785* Associates a string representing the specified double value with the786* specified key in this preference node. The associated string is the787* one that would be returned if the double value were passed to788* {@link Double#toString(double)}. This method is intended for use in789* conjunction with {@link #getDouble}.790*791* @param key key with which the string form of value is to be associated.792* @param value value whose string form is to be associated with key.793* @throws NullPointerException if {@code key} is {@code null}.794* @throws IllegalArgumentException if {@code key.length()} exceeds795* {@code MAX_KEY_LENGTH}.796* @throws IllegalStateException if this node (or an ancestor) has been797* removed with the {@link #removeNode()} method.798* @throws IllegalArgumentException if key contains799* the null control character, code point U+0000.800* @see #getDouble(String,double)801*/802public abstract void putDouble(String key, double value);803804/**805* Returns the double value represented by the string associated with the806* specified key in this preference node. The string is converted to an807* integer as by {@link Double#parseDouble(String)}. Returns the specified808* default if there is no value associated with the key, the backing store809* is inaccessible, or if {@code Double.parseDouble(String)} would throw a810* {@link NumberFormatException} if the associated value were passed.811* This method is intended for use in conjunction with {@link #putDouble}.812*813* <p>If the implementation supports <i>stored defaults</i> and such a814* default exists, is accessible, and could be converted to a double815* with {@code Double.parseDouble}, this double is returned in preference816* to the specified default.817*818* @param key key whose associated value is to be returned as a double.819* @param def the value to be returned in the event that this820* preference node has no value associated with {@code key}821* or the associated value cannot be interpreted as a double,822* or the backing store is inaccessible.823* @return the double value represented by the string associated with824* {@code key} in this preference node, or {@code def} if the825* associated value does not exist or cannot be interpreted as826* a double.827* @throws IllegalStateException if this node (or an ancestor) has been828* removed with the {@link #removeNode()} method.829* @throws NullPointerException if {@code key} is {@code null}.830* @throws IllegalArgumentException if key contains the null control831* character, code point U+0000.832* @see #putDouble(String,double)833* @see #get(String,String)834*/835public abstract double getDouble(String key, double def);836837/**838* Associates a string representing the specified byte array with the839* specified key in this preference node. The associated string is840* the <i>Base64</i> encoding of the byte array, as defined in <a841* href=http://www.ietf.org/rfc/rfc2045.txt>RFC 2045</a>, Section 6.8,842* with one minor change: the string will consist solely of characters843* from the <i>Base64 Alphabet</i>; it will not contain any newline844* characters. Note that the maximum length of the byte array is limited845* to three quarters of {@code MAX_VALUE_LENGTH} so that the length846* of the Base64 encoded String does not exceed {@code MAX_VALUE_LENGTH}.847* This method is intended for use in conjunction with848* {@link #getByteArray}.849*850* @param key key with which the string form of value is to be associated.851* @param value value whose string form is to be associated with key.852* @throws NullPointerException if key or value is {@code null}.853* @throws IllegalArgumentException if key.length() exceeds MAX_KEY_LENGTH854* or if value.length exceeds MAX_VALUE_LENGTH*3/4.855* @throws IllegalStateException if this node (or an ancestor) has been856* removed with the {@link #removeNode()} method.857* @throws IllegalArgumentException if key contains858* the null control character, code point U+0000.859* @see #getByteArray(String,byte[])860* @see #get(String,String)861*/862public abstract void putByteArray(String key, byte[] value);863864/**865* Returns the byte array value represented by the string associated with866* the specified key in this preference node. Valid strings are867* <i>Base64</i> encoded binary data, as defined in <a868* href=http://www.ietf.org/rfc/rfc2045.txt>RFC 2045</a>, Section 6.8,869* with one minor change: the string must consist solely of characters870* from the <i>Base64 Alphabet</i>; no newline characters or871* extraneous characters are permitted. This method is intended for use872* in conjunction with {@link #putByteArray}.873*874* <p>Returns the specified default if there is no value875* associated with the key, the backing store is inaccessible, or if the876* associated value is not a valid Base64 encoded byte array877* (as defined above).878*879* <p>If the implementation supports <i>stored defaults</i> and such a880* default exists and is accessible, it is used in preference to the881* specified default, unless the stored default is not a valid Base64882* encoded byte array (as defined above), in which case the883* specified default is used.884*885* @param key key whose associated value is to be returned as a byte array.886* @param def the value to be returned in the event that this887* preference node has no value associated with {@code key}888* or the associated value cannot be interpreted as a byte array,889* or the backing store is inaccessible.890* @return the byte array value represented by the string associated with891* {@code key} in this preference node, or {@code def} if the892* associated value does not exist or cannot be interpreted as893* a byte array.894* @throws IllegalStateException if this node (or an ancestor) has been895* removed with the {@link #removeNode()} method.896* @throws NullPointerException if {@code key} is {@code null}. (A897* {@code null} value for {@code def} <i>is</i> permitted.)898* @throws IllegalArgumentException if key contains the null control899* character, code point U+0000.900* @see #get(String,String)901* @see #putByteArray(String,byte[])902*/903public abstract byte[] getByteArray(String key, byte[] def);904905/**906* Returns all of the keys that have an associated value in this907* preference node. (The returned array will be of size zero if908* this node has no preferences.)909*910* <p>If the implementation supports <i>stored defaults</i> and there911* are any such defaults at this node that have not been overridden,912* by explicit preferences, the defaults are returned in the array in913* addition to any explicit preferences.914*915* @return an array of the keys that have an associated value in this916* preference node.917* @throws BackingStoreException if this operation cannot be completed918* due to a failure in the backing store, or inability to919* communicate with it.920* @throws IllegalStateException if this node (or an ancestor) has been921* removed with the {@link #removeNode()} method.922*/923public abstract String[] keys() throws BackingStoreException;924925/**926* Returns the names of the children of this preference node, relative to927* this node. (The returned array will be of size zero if this node has928* no children.)929*930* @return the names of the children of this preference node.931* @throws BackingStoreException if this operation cannot be completed932* due to a failure in the backing store, or inability to933* communicate with it.934* @throws IllegalStateException if this node (or an ancestor) has been935* removed with the {@link #removeNode()} method.936*/937public abstract String[] childrenNames() throws BackingStoreException;938939/**940* Returns the parent of this preference node, or {@code null} if this is941* the root.942*943* @return the parent of this preference node.944* @throws IllegalStateException if this node (or an ancestor) has been945* removed with the {@link #removeNode()} method.946*/947public abstract Preferences parent();948949/**950* Returns the named preference node in the same tree as this node,951* creating it and any of its ancestors if they do not already exist.952* Accepts a relative or absolute path name. Relative path names953* (which do not begin with the slash character {@code ('/')}) are954* interpreted relative to this preference node.955*956* <p>If the returned node did not exist prior to this call, this node and957* any ancestors that were created by this call are not guaranteed958* to become permanent until the {@code flush} method is called on959* the returned node (or one of its ancestors or descendants).960*961* @param pathName the path name of the preference node to return.962* @return the specified preference node.963* @throws IllegalArgumentException if the path name is invalid (i.e.,964* it contains multiple consecutive slash characters, or ends965* with a slash character and is more than one character long).966* @throws NullPointerException if path name is {@code null}.967* @throws IllegalStateException if this node (or an ancestor) has been968* removed with the {@link #removeNode()} method.969* @see #flush()970*/971public abstract Preferences node(String pathName);972973/**974* Returns true if the named preference node exists in the same tree975* as this node. Relative path names (which do not begin with the slash976* character {@code ('/')}) are interpreted relative to this preference977* node.978*979* <p>If this node (or an ancestor) has already been removed with the980* {@link #removeNode()} method, it <i>is</i> legal to invoke this method,981* but only with the path name {@code ""}; the invocation will return982* {@code false}. Thus, the idiom {@code p.nodeExists("")} may be983* used to test whether {@code p} has been removed.984*985* @param pathName the path name of the node whose existence986* is to be checked.987* @return true if the specified node exists.988* @throws BackingStoreException if this operation cannot be completed989* due to a failure in the backing store, or inability to990* communicate with it.991* @throws IllegalArgumentException if the path name is invalid (i.e.,992* it contains multiple consecutive slash characters, or ends993* with a slash character and is more than one character long).994* @throws NullPointerException if path name is {@code null}.995* @throws IllegalStateException if this node (or an ancestor) has been996* removed with the {@link #removeNode()} method and997* {@code pathName} is not the empty string ({@code ""}).998*/999public abstract boolean nodeExists(String pathName)1000throws BackingStoreException;10011002/**1003* Removes this preference node and all of its descendants, invalidating1004* any preferences contained in the removed nodes. Once a node has been1005* removed, attempting any method other than {@link #name()},1006* {@link #absolutePath()}, {@link #isUserNode()}, {@link #flush()} or1007* {@link #node(String) nodeExists("")} on the corresponding1008* {@code Preferences} instance will fail with an1009* {@code IllegalStateException}. (The methods defined on {@link Object}1010* can still be invoked on a node after it has been removed; they will not1011* throw {@code IllegalStateException}.)1012*1013* <p>The removal is not guaranteed to be persistent until the1014* {@code flush} method is called on this node (or an ancestor).1015*1016* <p>If this implementation supports <i>stored defaults</i>, removing a1017* node exposes any stored defaults at or below this node. Thus, a1018* subsequent call to {@code nodeExists} on this node's path name may1019* return {@code true}, and a subsequent call to {@code node} on this1020* path name may return a (different) {@code Preferences} instance1021* representing a non-empty collection of preferences and/or children.1022*1023* @throws BackingStoreException if this operation cannot be completed1024* due to a failure in the backing store, or inability to1025* communicate with it.1026* @throws IllegalStateException if this node (or an ancestor) has already1027* been removed with the {@link #removeNode()} method.1028* @throws UnsupportedOperationException if this method is invoked on1029* the root node.1030* @see #flush()1031*/1032public abstract void removeNode() throws BackingStoreException;10331034/**1035* Returns this preference node's name, relative to its parent.1036*1037* @return this preference node's name, relative to its parent.1038*/1039public abstract String name();10401041/**1042* Returns this preference node's absolute path name.1043*1044* @return this preference node's absolute path name.1045*/1046public abstract String absolutePath();10471048/**1049* Returns {@code true} if this preference node is in the user1050* preference tree, {@code false} if it's in the system preference tree.1051*1052* @return {@code true} if this preference node is in the user1053* preference tree, {@code false} if it's in the system1054* preference tree.1055*/1056public abstract boolean isUserNode();10571058/**1059* Returns a string representation of this preferences node,1060* as if computed by the expression:{@code (this.isUserNode() ? "User" :1061* "System") + " Preference Node: " + this.absolutePath()}.1062*/1063public abstract String toString();10641065/**1066* Forces any changes in the contents of this preference node and its1067* descendants to the persistent store. Once this method returns1068* successfully, it is safe to assume that all changes made in the1069* subtree rooted at this node prior to the method invocation have become1070* permanent.1071*1072* <p>Implementations are free to flush changes into the persistent store1073* at any time. They do not need to wait for this method to be called.1074*1075* <p>When a flush occurs on a newly created node, it is made persistent,1076* as are any ancestors (and descendants) that have yet to be made1077* persistent. Note however that any preference value changes in1078* ancestors are <i>not</i> guaranteed to be made persistent.1079*1080* <p> If this method is invoked on a node that has been removed with1081* the {@link #removeNode()} method, flushSpi() is invoked on this node,1082* but not on others.1083*1084* @throws BackingStoreException if this operation cannot be completed1085* due to a failure in the backing store, or inability to1086* communicate with it.1087* @see #sync()1088*/1089public abstract void flush() throws BackingStoreException;10901091/**1092* Ensures that future reads from this preference node and its1093* descendants reflect any changes that were committed to the persistent1094* store (from any VM) prior to the {@code sync} invocation. As a1095* side-effect, forces any changes in the contents of this preference node1096* and its descendants to the persistent store, as if the {@code flush}1097* method had been invoked on this node.1098*1099* @throws BackingStoreException if this operation cannot be completed1100* due to a failure in the backing store, or inability to1101* communicate with it.1102* @throws IllegalStateException if this node (or an ancestor) has been1103* removed with the {@link #removeNode()} method.1104* @see #flush()1105*/1106public abstract void sync() throws BackingStoreException;11071108/**1109* Registers the specified listener to receive <i>preference change1110* events</i> for this preference node. A preference change event is1111* generated when a preference is added to this node, removed from this1112* node, or when the value associated with a preference is changed.1113* (Preference change events are <i>not</i> generated by the {@link1114* #removeNode()} method, which generates a <i>node change event</i>.1115* Preference change events <i>are</i> generated by the {@code clear}1116* method.)1117*1118* <p>Events are only guaranteed for changes made within the same JVM1119* as the registered listener, though some implementations may generate1120* events for changes made outside this JVM. Events may be generated1121* before the changes have been made persistent. Events are not generated1122* when preferences are modified in descendants of this node; a caller1123* desiring such events must register with each descendant.1124*1125* @param pcl The preference change listener to add.1126* @throws NullPointerException if {@code pcl} is null.1127* @throws IllegalStateException if this node (or an ancestor) has been1128* removed with the {@link #removeNode()} method.1129* @see #removePreferenceChangeListener(PreferenceChangeListener)1130* @see #addNodeChangeListener(NodeChangeListener)1131*/1132public abstract void addPreferenceChangeListener(1133PreferenceChangeListener pcl);11341135/**1136* Removes the specified preference change listener, so it no longer1137* receives preference change events.1138*1139* @param pcl The preference change listener to remove.1140* @throws IllegalArgumentException if {@code pcl} was not a registered1141* preference change listener on this node.1142* @throws IllegalStateException if this node (or an ancestor) has been1143* removed with the {@link #removeNode()} method.1144* @see #addPreferenceChangeListener(PreferenceChangeListener)1145*/1146public abstract void removePreferenceChangeListener(1147PreferenceChangeListener pcl);11481149/**1150* Registers the specified listener to receive <i>node change events</i>1151* for this node. A node change event is generated when a child node is1152* added to or removed from this node. (A single {@link #removeNode()}1153* invocation results in multiple <i>node change events</i>, one for every1154* node in the subtree rooted at the removed node.)1155*1156* <p>Events are only guaranteed for changes made within the same JVM1157* as the registered listener, though some implementations may generate1158* events for changes made outside this JVM. Events may be generated1159* before the changes have become permanent. Events are not generated1160* when indirect descendants of this node are added or removed; a1161* caller desiring such events must register with each descendant.1162*1163* <p>Few guarantees can be made regarding node creation. Because nodes1164* are created implicitly upon access, it may not be feasible for an1165* implementation to determine whether a child node existed in the backing1166* store prior to access (for example, because the backing store is1167* unreachable or cached information is out of date). Under these1168* circumstances, implementations are neither required to generate node1169* change events nor prohibited from doing so.1170*1171* @param ncl The {@code NodeChangeListener} to add.1172* @throws NullPointerException if {@code ncl} is null.1173* @throws IllegalStateException if this node (or an ancestor) has been1174* removed with the {@link #removeNode()} method.1175* @see #removeNodeChangeListener(NodeChangeListener)1176* @see #addPreferenceChangeListener(PreferenceChangeListener)1177*/1178public abstract void addNodeChangeListener(NodeChangeListener ncl);11791180/**1181* Removes the specified {@code NodeChangeListener}, so it no longer1182* receives change events.1183*1184* @param ncl The {@code NodeChangeListener} to remove.1185* @throws IllegalArgumentException if {@code ncl} was not a registered1186* {@code NodeChangeListener} on this node.1187* @throws IllegalStateException if this node (or an ancestor) has been1188* removed with the {@link #removeNode()} method.1189* @see #addNodeChangeListener(NodeChangeListener)1190*/1191public abstract void removeNodeChangeListener(NodeChangeListener ncl);11921193/**1194* Emits on the specified output stream an XML document representing all1195* of the preferences contained in this node (but not its descendants).1196* This XML document is, in effect, an offline backup of the node.1197*1198* <p>The XML document will have the following DOCTYPE declaration:1199* <pre>{@code1200* <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">1201* }</pre>1202* The UTF-8 character encoding will be used.1203*1204* <p>This method is an exception to the general rule that the results of1205* concurrently executing multiple methods in this class yields1206* results equivalent to some serial execution. If the preferences1207* at this node are modified concurrently with an invocation of this1208* method, the exported preferences comprise a "fuzzy snapshot" of the1209* preferences contained in the node; some of the concurrent modifications1210* may be reflected in the exported data while others may not.1211*1212* @param os the output stream on which to emit the XML document.1213* @throws IOException if writing to the specified output stream1214* results in an {@code IOException}.1215* @throws BackingStoreException if preference data cannot be read from1216* backing store.1217* @see #importPreferences(InputStream)1218* @throws IllegalStateException if this node (or an ancestor) has been1219* removed with the {@link #removeNode()} method.1220*/1221public abstract void exportNode(OutputStream os)1222throws IOException, BackingStoreException;12231224/**1225* Emits an XML document representing all of the preferences contained1226* in this node and all of its descendants. This XML document is, in1227* effect, an offline backup of the subtree rooted at the node.1228*1229* <p>The XML document will have the following DOCTYPE declaration:1230* <pre>{@code1231* <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">1232* }</pre>1233* The UTF-8 character encoding will be used.1234*1235* <p>This method is an exception to the general rule that the results of1236* concurrently executing multiple methods in this class yields1237* results equivalent to some serial execution. If the preferences1238* or nodes in the subtree rooted at this node are modified concurrently1239* with an invocation of this method, the exported preferences comprise a1240* "fuzzy snapshot" of the subtree; some of the concurrent modifications1241* may be reflected in the exported data while others may not.1242*1243* @param os the output stream on which to emit the XML document.1244* @throws IOException if writing to the specified output stream1245* results in an {@code IOException}.1246* @throws BackingStoreException if preference data cannot be read from1247* backing store.1248* @throws IllegalStateException if this node (or an ancestor) has been1249* removed with the {@link #removeNode()} method.1250* @see #importPreferences(InputStream)1251* @see #exportNode(OutputStream)1252*/1253public abstract void exportSubtree(OutputStream os)1254throws IOException, BackingStoreException;12551256/**1257* Imports all of the preferences represented by the XML document on the1258* specified input stream. The document may represent user preferences or1259* system preferences. If it represents user preferences, the preferences1260* will be imported into the calling user's preference tree (even if they1261* originally came from a different user's preference tree). If any of1262* the preferences described by the document inhabit preference nodes that1263* do not exist, the nodes will be created.1264*1265* <p>The XML document must have the following DOCTYPE declaration:1266* <pre>{@code1267* <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">1268* }</pre>1269* (This method is designed for use in conjunction with1270* {@link #exportNode(OutputStream)} and1271* {@link #exportSubtree(OutputStream)}.1272*1273* <p>This method is an exception to the general rule that the results of1274* concurrently executing multiple methods in this class yields1275* results equivalent to some serial execution. The method behaves1276* as if implemented on top of the other public methods in this class,1277* notably {@link #node(String)} and {@link #put(String, String)}.1278*1279* @param is the input stream from which to read the XML document.1280* @throws IOException if reading from the specified input stream1281* results in an {@code IOException}.1282* @throws InvalidPreferencesFormatException Data on input stream does not1283* constitute a valid XML document with the mandated document type.1284* @throws SecurityException If a security manager is present and1285* it denies {@code RuntimePermission("preferences")}.1286* @see RuntimePermission1287*/1288public static void importPreferences(InputStream is)1289throws IOException, InvalidPreferencesFormatException1290{1291XmlSupport.importPreferences(is);1292}1293}129412951296