Class 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 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
    • Constructor Detail

      • NodeTracker

        public NodeTracker()
        Creates a new instance of NodeTracker. This instance does not yet track any nodes.
      • NodeTracker

        private NodeTracker​(java.util.Map<NodeSelector,​NodeTracker.TrackedNodeData> map)
        Creates a new instance of NodeTracker 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 in TrackedNodeData object. This method is called if a tracked node got cleared by a transaction.
        Parameters:
        data - the TrackedNodeData
        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 new TrackedNodeData 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 - the NodeSelector referencing the target node of the current transaction (may be null)
        e - the current selector and TrackedNodeData
        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 a TrackedNodeData 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 node
        txTarget - the NodeSelector referencing the target node of the current transaction (may be null)
        resolver - the NodeKeyResolver
        handler - the NodeHandler
        e - the current selector and TrackedNodeData
        Returns:
        the updated TrackedNodeData
      • 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 - the NodeSelector
        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 current ImmutableNode instance associated with the given selector.
        Parameters:
        selector - the NodeSelector
        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 the TrackedNodeData object for the specified selector. If the selector cannot be resolved, an exception is thrown.
        Parameters:
        selector - the NodeSelector
        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 - the NodeSelector
        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 - the NodeSelector
        newNode - the replacement node
        Returns:
        the updated instance
        Throws:
        ConfigurationRuntimeException - if the selector cannot be resolved
      • 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 the NodeSelector objects
        nodes - 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 - the NodeSelector
        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 in NodeSelector 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 node
        txTarget - the NodeSelector referencing the target node of the current transaction (may be null)
        resolver - the NodeKeyResolver
        handler - the NodeHandler
        Returns:
        the updated instance