Class NodeTracker
- java.lang.Object
-
- org.apache.commons.configuration2.tree.NodeTracker
-
final class NodeTracker extends java.lang.Object
A class which can track specific nodes in an
InMemoryNodeModel
.Sometimes it is necessary to keep track on a specific node, for instance when operating on a subtree of a model. For a model comprised of immutable nodes this is not trivial because each update of the model may cause the node to be replaced. So holding a direct pointer onto the target node is not an option; this instance may become outdated.
This class provides an API for selecting a specific node by using a
NodeSelector
. The selector is used to obtain an initial reference to the target node. It is then applied again after each update of the associated node model (which is done in theupdate()
method). At this point of time two things can happen:- The
NodeSelector
associated with the tracked node still selects a single node. Then this node becomes the new tracked node. This may be the same instance as before or a new one. - The selector does no longer find the target node. This can happen for instance if it has been removed by an operation. In this case, the previous node instance is used. It is now detached from the model, but can still be used for operations on this subtree. It may even become life again after another update of the model.
Implementation note: This class is intended to work in a concurrent environment. Instances are immutable. The represented state can be updated by creating new instances which are then stored by the owning node model.
- Since:
- 2.0
- The
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description private static class
NodeTracker.TrackedNodeData
A simple data class holding information about a tracked node.
-
Field Summary
Fields Modifier and Type Field Description private java.util.Map<NodeSelector,NodeTracker.TrackedNodeData>
trackedNodes
A map with data about tracked nodes.
-
Constructor Summary
Constructors Modifier Constructor Description NodeTracker()
Creates a new instance ofNodeTracker
.private
NodeTracker(java.util.Map<NodeSelector,NodeTracker.TrackedNodeData> map)
Creates a new instance ofNodeTracker
and initializes it with the given map of tracked nodes.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description private static ImmutableNode
createEmptyTrackedNode(NodeTracker.TrackedNodeData data)
Creates an empty node derived from the passed inTrackedNodeData
object.NodeTracker
detachAllTrackedNodes()
Marks all tracked nodes as detached.private static NodeTracker.TrackedNodeData
detachedTrackedNodeData(NodeSelector txTarget, java.util.Map.Entry<NodeSelector,NodeTracker.TrackedNodeData> e)
Creates a newTrackedNodeData
object for a tracked node which becomes detached within the current transaction.private static NodeTracker.TrackedNodeData
determineUpdatedTrackedNodeData(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler, java.util.Map.Entry<NodeSelector,NodeTracker.TrackedNodeData> e)
Returns aTrackedNodeData
object for an update operation.InMemoryNodeModel
getDetachedNodeModel(NodeSelector selector)
Gets the detached node model for the specified tracked node.ImmutableNode
getTrackedNode(NodeSelector selector)
Gets the currentImmutableNode
instance associated with the given selector.private NodeTracker.TrackedNodeData
getTrackedNodeData(NodeSelector selector)
Obtains theTrackedNodeData
object for the specified selector.boolean
isTrackedNodeDetached(NodeSelector selector)
Returns a flag whether the specified tracked node is detached.NodeTracker
replaceAndDetachTrackedNode(NodeSelector selector, ImmutableNode newNode)
Replaces a tracked node by another one.private static NodeTracker.TrackedNodeData
trackDataForAddedObserver(ImmutableNode root, NodeSelector selector, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler, NodeTracker.TrackedNodeData trackData)
Creates aTrackedNodeData
object for a newly added observer for the specified node selector.NodeTracker
trackNode(ImmutableNode root, NodeSelector selector, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler)
Adds a node to be tracked.NodeTracker
trackNodes(java.util.Collection<NodeSelector> selectors, java.util.Collection<ImmutableNode> nodes)
Adds a number of nodes to be tracked.NodeTracker
untrackNode(NodeSelector selector)
Notifies this object that an observer was removed for the specified tracked node.NodeTracker
update(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler)
Updates tracking information after the node structure has been changed.
-
-
-
Field Detail
-
trackedNodes
private final java.util.Map<NodeSelector,NodeTracker.TrackedNodeData> trackedNodes
A map with data about tracked nodes.
-
-
Constructor Detail
-
NodeTracker
public NodeTracker()
Creates a new instance ofNodeTracker
. This instance does not yet track any nodes.
-
NodeTracker
private NodeTracker(java.util.Map<NodeSelector,NodeTracker.TrackedNodeData> map)
Creates a new instance ofNodeTracker
and initializes it with the given map of tracked nodes. This constructor is used internally when the state of tracked nodes has changed.- Parameters:
map
- the map with tracked nodes
-
-
Method Detail
-
createEmptyTrackedNode
private static ImmutableNode createEmptyTrackedNode(NodeTracker.TrackedNodeData data)
Creates an empty node derived from the passed inTrackedNodeData
object. This method is called if a tracked node got cleared by a transaction.- Parameters:
data
- theTrackedNodeData
- Returns:
- the new node instance for this tracked node
-
detachedTrackedNodeData
private static NodeTracker.TrackedNodeData detachedTrackedNodeData(NodeSelector txTarget, java.util.Map.Entry<NodeSelector,NodeTracker.TrackedNodeData> e)
Creates a newTrackedNodeData
object for a tracked node which becomes detached within the current transaction. This method checks whether the affected node is the root node of the current transaction. If so, it is cleared.- Parameters:
txTarget
- theNodeSelector
referencing the target node of the current transaction (may be null)e
- the current selector andTrackedNodeData
- Returns:
- the new
TrackedNodeData
object to be used for this tracked node
-
determineUpdatedTrackedNodeData
private static NodeTracker.TrackedNodeData determineUpdatedTrackedNodeData(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler, java.util.Map.Entry<NodeSelector,NodeTracker.TrackedNodeData> e)
Returns aTrackedNodeData
object for an update operation. If the tracked node is still life, its selector is applied to the current root node. It may become detached if there is no match.- Parameters:
root
- the root nodetxTarget
- theNodeSelector
referencing the target node of the current transaction (may be null)resolver
- theNodeKeyResolver
handler
- theNodeHandler
e
- the current selector andTrackedNodeData
- Returns:
- the updated
TrackedNodeData
-
trackDataForAddedObserver
private static NodeTracker.TrackedNodeData trackDataForAddedObserver(ImmutableNode root, NodeSelector selector, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler, NodeTracker.TrackedNodeData trackData)
Creates aTrackedNodeData
object for a newly added observer for the specified node selector.- Parameters:
root
- the root nodeselector
- theNodeSelector
resolver
- theNodeKeyResolver
handler
- theNodeHandler
trackData
- the current data for this selector- Returns:
- the updated
TrackedNodeData
- Throws:
ConfigurationRuntimeException
- if the selector does not select a single node
-
detachAllTrackedNodes
public NodeTracker detachAllTrackedNodes()
Marks all tracked nodes as detached. This method is called if there are some drastic changes on the underlying node structure, e.g. if the root node was replaced.- Returns:
- the updated instance
-
getDetachedNodeModel
public InMemoryNodeModel getDetachedNodeModel(NodeSelector selector)
Gets the detached node model for the specified tracked node. When a node becomes detached, operations on it are independent from the original model. To implement this, a separate node model is created wrapping this tracked node. This model can be queried by this method. If the node affected is not detached, result is null.- Parameters:
selector
- theNodeSelector
- Returns:
- the detached node model for this node or null
- Throws:
ConfigurationRuntimeException
- if no data for this selector is available
-
getTrackedNode
public ImmutableNode getTrackedNode(NodeSelector selector)
Gets the currentImmutableNode
instance associated with the given selector.- Parameters:
selector
- theNodeSelector
- Returns:
- the
ImmutableNode
selected by this selector - Throws:
ConfigurationRuntimeException
- if no data for this selector is available
-
getTrackedNodeData
private NodeTracker.TrackedNodeData getTrackedNodeData(NodeSelector selector)
Obtains theTrackedNodeData
object for the specified selector. If the selector cannot be resolved, an exception is thrown.- Parameters:
selector
- theNodeSelector
- Returns:
- the
TrackedNodeData
object for this selector - Throws:
ConfigurationRuntimeException
- if the selector cannot be resolved
-
isTrackedNodeDetached
public boolean isTrackedNodeDetached(NodeSelector selector)
Returns a flag whether the specified tracked node is detached.- Parameters:
selector
- theNodeSelector
- Returns:
- a flag whether this node is detached
- Throws:
ConfigurationRuntimeException
- if no data for this selector is available
-
replaceAndDetachTrackedNode
public NodeTracker replaceAndDetachTrackedNode(NodeSelector selector, ImmutableNode newNode)
Replaces a tracked node by another one. This operation causes the tracked node to become detached.- Parameters:
selector
- theNodeSelector
newNode
- the replacement node- Returns:
- the updated instance
- Throws:
ConfigurationRuntimeException
- if the selector cannot be resolved
-
trackNode
public NodeTracker trackNode(ImmutableNode root, NodeSelector selector, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler)
Adds a node to be tracked. The passed in selector must select exactly one target node, otherwise an exception is thrown. A new instance is created with the updated tracking state.- Parameters:
root
- the root nodeselector
- theNodeSelector
resolver
- theNodeKeyResolver
handler
- theNodeHandler
- Returns:
- the updated instance
- Throws:
ConfigurationRuntimeException
- if the selector does not select a single node
-
trackNodes
public NodeTracker trackNodes(java.util.Collection<NodeSelector> selectors, java.util.Collection<ImmutableNode> nodes)
Adds a number of nodes to be tracked. For each node in the passed in collection, a tracked node entry is created unless already one exists.- Parameters:
selectors
- a collection with theNodeSelector
objectsnodes
- a collection with the nodes to be tracked- Returns:
- the updated instance
-
untrackNode
public NodeTracker untrackNode(NodeSelector selector)
Notifies this object that an observer was removed for the specified tracked node. If this was the last observer, the track data for this selector can be removed.- Parameters:
selector
- theNodeSelector
- Returns:
- the updated instance
- Throws:
ConfigurationRuntimeException
- if no information about this node is available
-
update
public NodeTracker update(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler)
Updates tracking information after the node structure has been changed. This method iterates over all tracked nodes. The selectors are evaluated again to update the node reference. If this fails for a selector, the previous node is reused; this tracked node is then detached. The passed inNodeSelector
is the selector of the tracked node which is the target of the current transaction. (It is null if the transaction is not executed on a tracked node.) This is used to handle a special case: if the tracked node becomes detached by an operation targeting itself, this means that the node has been cleared by this operation. In this case, the previous node instance is not used, but an empty node is created.- Parameters:
root
- the root nodetxTarget
- theNodeSelector
referencing the target node of the current transaction (may be null)resolver
- theNodeKeyResolver
handler
- theNodeHandler
- Returns:
- the updated instance
-
-