📄 abstractpreferences.java
字号:
/* * @(#)AbstractPreferences.java 1.17 03/01/23 * * Copyright 2003 Sun Microsystems, Inc. All rights reserved. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. */package java.util.prefs;import java.util.*;import java.io.*;import java.security.AccessController;import java.security.PrivilegedAction;// These imports needed only as a workaround for a JavaDoc bugimport java.lang.Integer;import java.lang.Long;import java.lang.Float;import java.lang.Double;/** * This class provides a skeletal implementation of the {@link Preferences} * class, greatly easing the task of implementing it. * * <p><strong>This class is for <tt>Preferences</tt> implementers only. * Normal users of the <tt>Preferences</tt> facility should have no need to * consult this documentation. The {@link Preferences} documentation * should suffice.</strong> * * <p>Implementors must override the nine abstract service-provider interface * (SPI) methods: {@link #getSpi(String)}, {@link #putSpi(String,String)}, * {@link #removeSpi(String)}, {@link #childSpi(String)}, {@link * #removeNodeSpi()}, {@link #keysSpi()}, {@link #childrenNamesSpi()}, {@link * #syncSpi()} and {@link #flushSpi()}. All of the concrete methods specify * precisely how they are implemented atop these SPI methods. The implementor * may, at his discretion, override one or more of the concrete methods if the * default implementation is unsatisfactory for any reason, such as * performance. * * <p>The SPI methods fall into three groups concerning exception * behavior. The <tt>getSpi</tt> method should never throw exceptions, but it * doesn't really matter, as any exception thrown by this method will be * intercepted by {@link #get(String,String)}, which will return the specified * default value to the caller. The <tt>removeNodeSpi, keysSpi, * childrenNamesSpi, syncSpi</tt> and <tt>flushSpi</tt> methods are specified * to throw {@link BackingStoreException}, and the implementation is required * to throw this checked exception if it is unable to perform the operation. * The exception propagates outward, causing the corresponding API method * to fail. * * <p>The remaining SPI methods {@link #putSpi(String,String)}, {@link * #removeSpi(String)} and {@link #childSpi(String)} have more complicated * exception behavior. They are not specified to throw * <tt>BackingStoreException</tt>, as they can generally obey their contracts * even if the backing store is unavailable. This is true because they return * no information and their effects are not required to become permanent until * a subsequent call to {Preferences#flush()} or * {Preferences#sync()}. Generally speaking, these SPI methods should not * throw exceptions. In some implementations, there may be circumstances * under which these calls cannot even enqueue the requested operation for * later processing. Even under these circumstances it is generally better to * simply ignore the invocation and return, rather than throwing an an * exception. Under these circumstances, however, all subsequent invocations * of <tt>flush()</tt> and <tt>sync</tt> should return <tt>false</tt>, as * returning <tt>true</tt> would imply that all previous operations had * successfully been made permanent. * * <p>There is one circumstance under which <tt>putSpi, removeSpi and * childSpi</tt> <i>should</i> throw an exception: if the caller lacks * sufficient privileges on the underlying operating system to perform the * requested operation. This will, for instance, occur on most systems * if a non-privileged user attempts to modify system preferences. * (The required privileges will vary from implementation to * implementation. On some implementations, they are the right to modify the * contents of some directory in the file system; on others they are the right * to modify contents of some key in a registry.) Under any of these * circumstances, it would generally be undesirable to let the program * continue executing as if these operations would become permanent at a later * time. While implementations are not required to throw an exception under * these circumstances, they are encouraged to do so. A {@link * SecurityException} would be appropriate. * * <p>Most of the SPI methods require the implementation to read or write * information at a preferences node. The implementor should beware of the * fact that another VM may have concurrently deleted this node from the * backing store. It is the implementation's responsibility to recreate the * node if it has been deleted. * * <p>Implementation note: In Sun's default <tt>Preferences</tt> * implementations, the user's identity is inherited from the underlying * operating system and does not change for the lifetime of the virtual * machine. It is recognized that server-side <tt>Preferences</tt> * implementations may have the user identity change from request to request, * implicitly passed to <tt>Preferences</tt> methods via the use of a * static {@link ThreadLocal} instance. Authors of such implementations are * <i>strongly</i> encouraged to determine the user at the time preferences * are accessed (for example by the {@link #get(String,String)} or {@link * #put(String,String)} method) rather than permanently associating a user * with each <tt>Preferences</tt> instance. The latter behavior conflicts * with normal <tt>Preferences</tt> usage and would lead to great confusion. * * @author Josh Bloch * @version 1.17, 01/23/03 * @see Preferences * @since 1.4 */public abstract class AbstractPreferences extends Preferences { /** * Our name relative to parent. */ private final String name; /** * Our absolute path name. */ private final String absolutePath; /** * Our parent node. */ final AbstractPreferences parent; /** * Our root node. */ private final AbstractPreferences root; // Relative to this node /** * This field should be <tt>true</tt> if this node did not exist in the * backing store prior to the creation of this object. The field * is initialized to false, but may be set to true by a subclass * constructor (and should not be modified thereafter). This field * indicates whether a node change event should be fired when * creation is complete. */ protected boolean newNode = false; /** * All known unremoved children of this node. (This "cache" is consulted * prior to calling childSpi() or getChild(). */ private Map kidCache = new HashMap(); /** * This field is used to keep track of whether or not this node has * been removed. Once it's set to true, it will never be reset to false. */ private boolean removed = false; /** * Registered preference change listeners. */ private PreferenceChangeListener[] prefListeners = new PreferenceChangeListener[0]; /** * Registered node change listeners. */ private NodeChangeListener[] nodeListeners = new NodeChangeListener[0]; /** * An object whose monitor is used to lock this node. This object * is used in preference to the node itself to reduce the likelihood of * intentional or unintentional denial of service due to a locked node. * To avoid deadlock, a node is <i>never</i> locked by a thread that * holds a lock on a descendant of that node. */ protected final Object lock = new Object(); /** * Creates a preference node with the specified parent and the specified * name relative to its parent. * * @param parent the parent of this preference node, or null if this * is the root. * @param name the name of this preference node, relative to its parent, * or <tt>""</tt> if this is the root. * @throws IllegalArgumentException if <tt>name</tt> contains a slash * (<tt>'/'</tt>), or <tt>parent</tt> is <tt>null</tt> and * name isn't <tt>""</tt>. */ protected AbstractPreferences(AbstractPreferences parent, String name) { if (parent==null) { if (!name.equals("")) throw new IllegalArgumentException("Root name '"+name+ "' must be \"\""); this.absolutePath = "/"; root = this; } else { if (name.indexOf('/') != -1) throw new IllegalArgumentException("Name '" + name + "' contains '/'"); if (name.equals("")) throw new IllegalArgumentException("Illegal name: empty string"); root = parent.root; absolutePath = (parent==root ? "/" + name : parent.absolutePath() + "/" + name); } this.name = name; this.parent = parent; } /** * Implements the <tt>put</tt> method as per the specification in * {@link Preferences#put(String,String)}. * * <p>This implementation checks that the key and value are legal, * obtains this preference node's lock, checks that the node * has not been removed, invokes {@link #putSpi(String,String)}, and if * there are any preference change listeners, enqueues a notification * event for processing by the event dispatch thread. * * @param key key with which the specified value is to be associated. * @param value value to be associated with the specified key. * @throws NullPointerException if key or value is <tt>null</tt>. * @throws IllegalArgumentException if <tt>key.length()</tt> exceeds * <tt>MAX_KEY_LENGTH</tt> or if <tt>value.length</tt> exceeds * <tt>MAX_VALUE_LENGTH</tt>. * @throws IllegalStateException if this node (or an ancestor) has been * removed with the {@link #removeNode()} method. */ public void put(String key, String value) { if (key==null || value==null) throw new NullPointerException(); if (key.length() > MAX_KEY_LENGTH) throw new IllegalArgumentException("Key too long: "+key); if (value.length() > MAX_VALUE_LENGTH) throw new IllegalArgumentException("Value too long: "+value); synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); putSpi(key, value); enqueuePreferenceChangeEvent(key, value); } } /** * Implements the <tt>get</tt> method as per the specification in * {@link Preferences#get(String,String)}. * * <p>This implementation first checks to see if <tt>key</tt> is * <tt>null</tt> throwing a <tt>NullPointerException</tt> if this is * the case. Then it obtains this preference node's lock, * checks that the node has not been removed, invokes {@link * #getSpi(String)}, and returns the result, unless the <tt>getSpi</tt> * invocation returns <tt>null</tt> or throws an exception, in which case * this invocation returns <tt>def</tt>. * * @param key key whose associated value is to be returned. * @param def the value to be returned in the event that this * preference node has no value associated with <tt>key</tt>. * @return the value associated with <tt>key</tt>, or <tt>def</tt> * if no value is associated with <tt>key</tt>. * @throws IllegalStateException if this node (or an ancestor) has been * removed with the {@link #removeNode()} method. * @throws NullPointerException if key is <tt>null</tt>. (A * <tt>null</tt> default <i>is</i> permitted.) */ public String get(String key, String def) { if (key==null) throw new NullPointerException("Null key"); synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); String result = null; try { result = getSpi(key); } catch (Exception e) { // Ignoring exception causes default to be returned } return (result==null ? def : result); } } /** * Implements the <tt>remove(String)</tt> method as per the specification * in {@link Preferences#remove(String)}. * * <p>This implementation obtains this preference node's lock, * checks that the node has not been removed, invokes * {@link #removeSpi(String)} and if there are any preference * change listeners, enqueues a notification event for processing by the * event dispatch thread. * * @param key key whose mapping is to be removed from the preference node. * @throws IllegalStateException if this node (or an ancestor) has been * removed with the {@link #removeNode()} method. */ public void remove(String key) { synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed.");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -