Class NodeTracker
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 the update()
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
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionprivate static class
A simple data class holding information about a tracked node. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate final Map<NodeSelector,
NodeTracker.TrackedNodeData> A map with data about tracked nodes. -
Constructor Summary
ConstructorsModifierConstructorDescriptionCreates a new instance ofNodeTracker
.private
Creates a new instance ofNodeTracker
and initializes it with the given map of tracked nodes. -
Method Summary
Modifier and TypeMethodDescriptionprivate static ImmutableNode
Creates an empty node derived from the passed inTrackedNodeData
object.Marks all tracked nodes as detached.private static NodeTracker.TrackedNodeData
detachedTrackedNodeData
(NodeSelector txTarget, 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, Map.Entry<NodeSelector, NodeTracker.TrackedNodeData> e) Returns aTrackedNodeData
object for an update operation.getDetachedNodeModel
(NodeSelector selector) Gets the detached node model for the specified tracked node.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.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.trackNode
(ImmutableNode root, NodeSelector selector, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler) Adds a node to be tracked.trackNodes
(Collection<NodeSelector> selectors, Collection<ImmutableNode> nodes) Adds a number of nodes to be tracked.untrackNode
(NodeSelector selector) Notifies this object that an observer was removed for the specified tracked node.update
(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler) Updates tracking information after the node structure has been changed.
-
Field Details
-
trackedNodes
A map with data about tracked nodes.
-
-
Constructor Details
-
NodeTracker
public NodeTracker()Creates a new instance ofNodeTracker
. This instance does not yet track any nodes. -
NodeTracker
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 Details
-
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
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
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
-
getTrackedNode
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
-
isTrackedNodeDetached
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
-
getDetachedNodeModel
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
-
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
-
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
-
replaceAndDetachTrackedNode
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
-
getTrackedNodeData
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
-
determineUpdatedTrackedNodeData
private static NodeTracker.TrackedNodeData determineUpdatedTrackedNodeData(ImmutableNode root, NodeSelector txTarget, NodeKeyResolver<ImmutableNode> resolver, NodeHandler<ImmutableNode> handler, 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
-
detachedTrackedNodeData
private static NodeTracker.TrackedNodeData detachedTrackedNodeData(NodeSelector txTarget, 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
-
createEmptyTrackedNode
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
-
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
-