📄 abstractpreferences.java
字号:
path.nextToken(); // Consume slash if (!path.hasMoreTokens()) throw new IllegalArgumentException("Path ends with slash"); return child.nodeExists(path); } } /** * Implements the <tt>removeNode()</tt> method as per the specification in * {@link Preferences#removeNode()}. * * <p>This implementation checks to see that this node is the root; if so, * it throws an appropriate exception. Then, it locks this node's parent, * and calls a recursive helper method that traverses the subtree rooted at * this node. The recursive method locks the node on which it was called, * checks that it has not already been removed, and then ensures that all * of its children are cached: The {@link #childrenNamesSpi()} method is * invoked and each returned child name is checked for containment in the * child-cache. If a child is not already cached, the {@link * #childSpi(String)} method is invoked to create a <tt>Preferences</tt> * instance for it, and this instance is put into the child-cache. Then * the helper method calls itself recursively on each node contained in its * child-cache. Next, it invokes {@link #removeNodeSpi()}, marks itself * as removed, and removes itself from its parent's child-cache. Finally, * if there are any node change listeners, it enqueues a notification * event for processing by the event dispatch thread. * * <p>Note that the helper method is always invoked with all ancestors up * to the "closest non-removed ancestor" locked. * * @throws IllegalStateException if this node (or an ancestor) has already * been removed with the {@link #removeNode()} method. * @throws UnsupportedOperationException if this method is invoked on * the root node. * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. */ public void removeNode() throws BackingStoreException { if (this==root) throw new UnsupportedOperationException("Can't remove the root!"); synchronized(parent.lock) { removeNode2(); parent.kidCache.remove(name); } } /* * Called with locks on all nodes on path from parent of "removal root" * to this (including the former but excluding the latter). */ private void removeNode2() throws BackingStoreException { synchronized(lock) { if (removed) throw new IllegalStateException("Node already removed."); // Ensure that all children are cached String[] kidNames = childrenNamesSpi(); for (int i=0; i<kidNames.length; i++) if (!kidCache.containsKey(kidNames[i])) kidCache.put(kidNames[i], childSpi(kidNames[i])); // Recursively remove all cached children for (Iterator i=kidCache.values().iterator(); i.hasNext(); ) ((AbstractPreferences)i.next()).removeNode2(); kidCache.clear(); // Now we have no descendants - it's time to die! removeNodeSpi(); removed = true; parent.enqueueNodeRemovedEvent(this); } } /** * Implements the <tt>name</tt> method as per the specification in * {@link Preferences#name()}. * * <p>This implementation merely returns the name that was * passed to this node's constructor. * * @return this preference node's name, relative to its parent. */ public String name() { return name; } /** * Implements the <tt>absolutePath</tt> method as per the specification in * {@link Preferences#absolutePath()}. * * <p>This implementation merely returns the absolute path name that * was computed at the time that this node was constructed (based on * the name that was passed to this node's constructor, and the names * that were passed to this node's ancestors' constructors). * * @return this preference node's absolute path name. */ public String absolutePath() { return absolutePath; } /** * Implements the <tt>isUserNode</tt> method as per the specification in * {@link Preferences#isUserNode()}. * * <p>This implementation compares this node's root node (which is stored * in a private field) with the value returned by * {@link Preferences#userRoot()}. If the two object references are * identical, this method returns true. * * @return <tt>true</tt> if this preference node is in the user * preference tree, <tt>false</tt> if it's in the system * preference tree. */ public boolean isUserNode() { Boolean result = (Boolean) AccessController.doPrivileged( new PrivilegedAction() { public Object run() { return new Boolean(root == Preferences.userRoot()); } }); return result.booleanValue(); } public void addPreferenceChangeListener(PreferenceChangeListener pcl) { if (pcl==null) throw new NullPointerException("Change listener is null."); synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); // Copy-on-write PreferenceChangeListener[] old = prefListeners; prefListeners = new PreferenceChangeListener[old.length + 1]; System.arraycopy(old, 0, prefListeners, 0, old.length); prefListeners[old.length] = pcl; } startEventDispatchThreadIfNecessary(); } public void removePreferenceChangeListener(PreferenceChangeListener pcl) { synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); if ((prefListeners == null) || (prefListeners.length == 0)) throw new IllegalArgumentException("Listener not registered."); // Copy-on-write PreferenceChangeListener[] newPl = new PreferenceChangeListener[prefListeners.length - 1]; int i = 0; while (i < newPl.length && prefListeners[i] != pcl) newPl[i] = prefListeners[i++]; if (i == newPl.length && prefListeners[i] != pcl) throw new IllegalArgumentException("Listener not registered."); while (i < newPl.length) newPl[i] = prefListeners[++i]; prefListeners = newPl; } } public void addNodeChangeListener(NodeChangeListener ncl) { if (ncl==null) throw new NullPointerException("Change listener is null."); synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); // Copy-on-write if (nodeListeners == null) { nodeListeners = new NodeChangeListener[1]; nodeListeners[0] = ncl; } else { NodeChangeListener[] old = nodeListeners; nodeListeners = new NodeChangeListener[old.length + 1]; System.arraycopy(old, 0, nodeListeners, 0, old.length); nodeListeners[old.length] = ncl; } } startEventDispatchThreadIfNecessary(); } public void removeNodeChangeListener(NodeChangeListener ncl) { synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed."); if ((nodeListeners == null) || (nodeListeners.length == 0)) throw new IllegalArgumentException("Listener not registered."); // Copy-on-write NodeChangeListener[] newNl = new NodeChangeListener[nodeListeners.length - 1]; int i = 0; while (i < nodeListeners.length && nodeListeners[i] != ncl) newNl[i] = nodeListeners[i++]; if (i == nodeListeners.length) throw new IllegalArgumentException("Listener not registered."); while (i < newNl.length) newNl[i] = nodeListeners[++i]; nodeListeners = newNl; } } // "SPI" METHODS /** * Put the given key-value association into this preference node. It is * guaranteed that <tt>key</tt> and <tt>value</tt> are non-null and of * legal length. Also, it is guaranteed that this node has not been * removed. (The implementor needn't check for any of these things.) * * <p>This method is invoked with the lock on this node held. */ protected abstract void putSpi(String key, String value); /** * Return the value associated with the specified key at this preference * node, or <tt>null</tt> if there is no association for this key, or the * association cannot be determined at this time. It is guaranteed that * <tt>key</tt> is non-null. Also, it is guaranteed that this node has * not been removed. (The implementor needn't check for either of these * things.) * * <p> Generally speaking, this method should not throw an exception * under any circumstances. If, however, if it does throw an exception, * the exception will be intercepted and treated as a <tt>null</tt> * return value. * * <p>This method is invoked with the lock on this node held. * * @return the value associated with the specified key at this preference * node, or <tt>null</tt> if there is no association for this * key, or the association cannot be determined at this time. */ protected abstract String getSpi(String key); /** * Remove the association (if any) for the specified key at this * preference node. It is guaranteed that <tt>key</tt> is non-null. * Also, it is guaranteed that this node has not been removed. * (The implementor needn't check for either of these things.) * * <p>This method is invoked with the lock on this node held. */ protected abstract void removeSpi(String key); /** * Removes this preference node, invalidating it and any preferences that * it contains. The named child will have no descendants at the time this * invocation is made (i.e., the {@link Preferences#removeNode()} method * invokes this method repeatedly in a bottom-up fashion, removing each of * a node's descendants before removing the node itself). * * <p>This method is invoked with the lock held on this node and its * parent (and all ancestors that are being removed as a * result of a single invocation to {@link Preferences#removeNode()}). * * <p>The removal of a node needn't become persistent until the * <tt>flush</tt> method is invoked on this node (or an ancestor). * * <p>If this node throws a <tt>BackingStoreException</tt>, the exception * will propagate out beyond the enclosing {@link #removeNode()} * invocation. * * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. */ protected abstract void removeNodeSpi() throws BackingStoreException; /** * Returns all of the keys that have an associated value in this * preference node. (The returned array will be of size zero if * this node has no preferences.) It is guaranteed that this node has not * been removed. * * <p>This method is invoked with the lock on this node held. * * <p>If this node throws a <tt>BackingStoreException</tt>, the exception * will propagate out beyond the enclosing {@link #keys()} invocation. * * @return an array of the keys that have an associated value in this * preference node. * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. */ protected abstract String[] keysSpi() throws BackingStoreException; /** * Returns the names of the children of this preference node. (The
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -