📄 abstractpreferences.java
字号:
* returned array will be of size zero if this node has no children.) * This method need not return the names of any nodes already cached, * but may do so without harm. * * <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 #childrenNames()} * invocation. * * @return an array containing the names of the children of 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[] childrenNamesSpi() throws BackingStoreException; /** * Returns the named child if it exists, or <tt>null</tt> if it does not. * It is guaranteed that <tt>nodeName</tt> is non-null, non-empty, * does not contain the slash character ('/'), and is no longer than * {@link #MAX_NAME_LENGTH} characters. Also, it is guaranteed * that this node has not been removed. (The implementor needn't check * for any of these things if he chooses to override this method.) * * <p>Finally, it is guaranteed that the named node has not been returned * by a previous invocation of this method or {@link #childSpi} after the * last time that it was removed. In other words, a cached value will * always be used in preference to invoking this method. (The implementor * needn't maintain his own cache of previously returned children if he * chooses to override this method.) * * <p>This implementation obtains this preference node's lock, invokes * {@link #childrenNames()} to get an array of the names of this node's * children, and iterates over the array comparing the name of each child * with the specified node name. If a child node has the correct name, * the {@link #childSpi(String)} method is invoked and the resulting * node is returned. If the iteration completes without finding the * specified name, <tt>null</tt> is returned. * * @param nodeName name of the child to be searched for. * @return the named child if it exists, or null if it does not. * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. */ protected AbstractPreferences getChild(String nodeName) throws BackingStoreException { synchronized(lock) { // assert kidCache.get(nodeName)==null; String[] kidNames = childrenNames(); for (int i=0; i<kidNames.length; i++) if (kidNames[i].equals(nodeName)) return childSpi(kidNames[i]); } return null; } /** * Returns the named child of this preference node, creating it if it does * not already exist. It is guaranteed that <tt>name</tt> is non-null, * non-empty, does not contain the slash character ('/'), and is no longer * than {@link #MAX_NAME_LENGTH} characters. Also, it is guaranteed that * this node has not been removed. (The implementor needn't check for any * of these things.) * * <p>Finally, it is guaranteed that the named node has not been returned * by a previous invocation of this method or {@link #getChild(String)} * after the last time that it was removed. In other words, a cached * value will always be used in preference to invoking this method. * Subclasses need not maintain their own cache of previously returned * children. * * <p>The implementer must ensure that the returned node has not been * removed. If a like-named child of this node was previously removed, the * implementer must return a newly constructed <tt>AbstractPreferences</tt> * node; once removed, an <tt>AbstractPreferences</tt> node * cannot be "resuscitated." * * <p>If this method causes a node to be created, this node is not * guaranteed to be persistent until the <tt>flush</tt> method is * invoked on this node or one of its ancestors (or descendants). * * <p>This method is invoked with the lock on this node held. * * @param name The name of the child node to return, relative to * this preference node. * @return The named child node. */ protected abstract AbstractPreferences childSpi(String name); /** * Returns the absolute path name of this preferences node. */ public String toString() { return (this.isUserNode() ? "User" : "System") + " Preference Node: " + this.absolutePath(); } /** * Implements the <tt>sync</tt> method as per the specification in * {@link Preferences#sync()}. * * <p>This implementation calls a recursive helper method that locks this * node, invokes syncSpi() on it, unlocks this node, and recursively * invokes this method on each "cached child." A cached child is a child * of this node that has been created in this VM and not subsequently * removed. In effect, this method does a depth first traversal of the * "cached subtree" rooted at this node, calling syncSpi() on each node in * the subTree while only that node is locked. Note that syncSpi() is * invoked top-down. * * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. * @throws IllegalStateException if this node (or an ancestor) has been * removed with the {@link #removeNode()} method. * @see #flush() */ public void sync() throws BackingStoreException { sync2(); } private void sync2() throws BackingStoreException { AbstractPreferences[] cachedKids; synchronized(lock) { if (removed) throw new IllegalStateException("Node has been removed"); syncSpi(); cachedKids = cachedChildren(); } for (int i=0; i<cachedKids.length; i++) cachedKids[i].sync2(); } /** * This method is invoked with this node locked. The contract of this * method is to synchronize any cached preferences stored at this node * with any stored in the backing store. (It is perfectly possible that * this node does not exist on the backing store, either because it has * been deleted by another VM, or because it has not yet been created.) * Note that this method should <i>not</i> synchronize the preferences in * any subnodes of this node. If the backing store naturally syncs an * entire subtree at once, the implementer is encouraged to override * sync(), rather than merely overriding this method. * * <p>If this node throws a <tt>BackingStoreException</tt>, the exception * will propagate out beyond the enclosing {@link #sync()} 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 syncSpi() throws BackingStoreException; /** * Implements the <tt>flush</tt> method as per the specification in * {@link Preferences#flush()}. * * <p>This implementation calls a recursive helper method that locks this * node, invokes flushSpi() on it, unlocks this node, and recursively * invokes this method on each "cached child." A cached child is a child * of this node that has been created in this VM and not subsequently * removed. In effect, this method does a depth first traversal of the * "cached subtree" rooted at this node, calling flushSpi() on each node in * the subTree while only that node is locked. Note that flushSpi() is * invoked top-down. * * <p> If this method is invoked on a node that has been removed with * the {@link #removeNode()} method, flushSpi() is invoked on this node, * but not on others. * * @throws BackingStoreException if this operation cannot be completed * due to a failure in the backing store, or inability to * communicate with it. * @see #flush() */ public void flush() throws BackingStoreException { flush2(); } private void flush2() throws BackingStoreException { AbstractPreferences[] cachedKids; synchronized(lock) { flushSpi(); if(removed) return; cachedKids = cachedChildren(); } for (int i = 0; i < cachedKids.length; i++) cachedKids[i].flush2(); } /** * This method is invoked with this node locked. The contract of this * method is to force any cached changes in the contents of this * preference node to the backing store, guaranteeing their persistence. * (It is perfectly possible that this node does not exist on the backing * store, either because it has been deleted by another VM, or because it * has not yet been created.) Note that this method should <i>not</i> * flush the preferences in any subnodes of this node. If the backing * store naturally flushes an entire subtree at once, the implementer is * encouraged to override flush(), rather than merely overriding this * method. * * <p>If this node throws a <tt>BackingStoreException</tt>, the exception * will propagate out beyond the enclosing {@link #flush()} 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 flushSpi() throws BackingStoreException; /** * Returns <tt>true</tt> iff this node (or an ancestor) has been * removed with the {@link #removeNode()} method. This method * locks this node prior to returning the contents of the private * field used to track this state. * * @return <tt>true</tt> iff this node (or an ancestor) has been * removed with the {@link #removeNode()} method. */ protected boolean isRemoved() { synchronized(lock) { return removed; } } /** * Queue of pending notification events. When a preference or node * change event for which there are one or more listeners occurs, * it is placed on this queue and the queue is notified. A background * thread waits on this queue and delivers the events. This decouples * event delivery from preference activity, greatly simplifying * locking and reducing opportunity for deadlock. */ private static final List eventQueue = new LinkedList(); /** * These two classes are used to distinguish NodeChangeEvents on * eventQueue so the event dispatch thread knows whether to call * childAdded or childRemoved. */ private class NodeAddedEvent extends NodeChangeEvent { NodeAddedEvent(Preferences parent, Preferences child) { super(parent, child); } } private class NodeRemovedEvent extends NodeChangeEvent { NodeRemovedEvent(Preferences parent, Preferences child) { super(parent, child); } } /** * A single background thread ("the event notification thread") monitors * the event queue and delivers events that are placed on the queue. */ private static class EventDispatchThread extends Thread { public void run() { while(true) { // Wait on eventQueue till an event is present EventObject event = null; synchronized(eventQueue) { try { while (eventQueue.isEmpty()) eventQueue.wait(); event = (EventObject) eventQueue.remove(0); } catch (InterruptedException e) { // XXX Log "Event dispatch thread interrupted. Exiting" return; } } // Now we have event & hold no locks; deliver evt to listeners AbstractPreferences src=(AbstractPreferences)event.getSource(); if (event instanceof PreferenceChangeEvent) { PreferenceChangeEvent pce = (PreferenceChangeEvent)event; PreferenceChangeListener[] listeners = src.prefListeners(); for (int i=0; i<listeners.length; i++) listeners[i].preferenceChange(pce); } else { NodeChangeEvent nce = (NodeChangeEvent)event; NodeChangeListener[] listeners = src.nodeListeners(); if (nce instanceof NodeAddedEvent) { for (int i=0; i<listeners.length; i++) listeners[i].childAdded(nce);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -