/// <summary>
        ///     Resolves a locator starting from 'startFrom' and the locator part at
        ///     position 'offset' in the locator.  This method is called from the 
        ///     DescendentsWalker.  It maintains the state of resolution as each
        ///     node is visited and individual locator parts are resolved.
        /// </summary>
        /// <param name="dependencyObject">the current node to visit</param>
        /// <param name="data">data containing the state of the current resolution</param>
        /// <returns>whether or not the children of this node should be visited</returns>
        private bool ResolveLocatorPart(DependencyObject dependencyObject, ResolvingLocatorState data, bool visitedViaVisualTree)
        {
            if (data.Finished)
                return false;

            ContentLocator locator = data.ContentLocatorBase;

            Debug.Assert(locator != null, "locator can not be null");
            Debug.Assert(data.LocatorPartIndex >= 0 && data.LocatorPartIndex < locator.Parts.Count,
                         "LocatorPartIndex out of range");

            bool keepResolving = true;
            DependencyObject node = null;
            SubTreeProcessor processor = null;
            ContentLocatorPart locatorPart = locator.Parts[data.LocatorPartIndex];
            if (locatorPart == null)
            {
                // Can't resolve a null ContentLocatorPart
                keepResolving = false;
            }
               
            processor = this.GetSubTreeProcessorForLocatorPart(locatorPart);            
            if (processor == null)
            {
                // Can't keep resolving if there is no processor for this ContentLocatorBase Part                
                keepResolving = false;
            }

            if (locatorPart != null && processor != null)
            {
                node = processor.ResolveLocatorPart(locatorPart, dependencyObject, out keepResolving);
                if (node != null)
                {
                    // At a minimum we are incompletely resolved
                    data.AttachmentLevel = AttachmentLevel.Incomplete;
                    data.AttachedAnchor = node;
                    keepResolving = true;

                    data.LastNodeMatched = node;
                    data.LocatorPartIndex++;

                    // We might already be finished here - if there are no more locator parts
                    // we are fully resolved and there's no need to keep resolving
                    if (data.LocatorPartIndex == locator.Parts.Count)
                    {
                        data.AttachmentLevel = AttachmentLevel.Full;
                        data.AttachedAnchor = node;
                        keepResolving = false;
                    }

                    // If all we have left is the last locatorPart, lets try to resolve it as a
                    // selection. If there is no selection processor, it will fall through and
                    // be handled by resolving on one of the children
                    else if (data.LocatorPartIndex == locator.Parts.Count - 1)
                    {
                        locatorPart = locator.Parts[data.LocatorPartIndex];

                        SelectionProcessor selProcessor = GetSelectionProcessorForLocatorPart(locatorPart);
                        if (selProcessor != null)
                        {
                            AttachmentLevel attachmentLevel;
                            Object selection = selProcessor.ResolveLocatorPart(locatorPart, node, out attachmentLevel);
                            if (selection != null)
                            {
                                data.AttachmentLevel = attachmentLevel;
                                data.AttachedAnchor = selection;
                                keepResolving = false;
                            }
                            else
                            {
                                // In this case the processor couldn't resolve the selection
                                // locator part.  There's no use in continuing.
                                keepResolving = false;
                            }
                        }
                    }
                }
            }
            
            return keepResolving;
        }
        /// <summary>
        ///     This gets called after each node's subtree has been called to resolve
        ///     the current locator part.
        ///     If the node the call is made for was the last node that anything was
        ///     matched with, we want to stop looking at the rest of the tree.  This is
        ///     because matches should be unique (so no sibling should be able to match)
        ///     and if they aren't the first match wins.
        /// </summary>
        /// <param name="dependencyObject"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        private bool TerminateResolve(DependencyObject dependencyObject, ResolvingLocatorState data, bool visitedViaVisualTree)
        {
            // If we are finished with the subtree for the last node matched, we've
            // resolved as much as we can and we should not bother looking at the
            // rest of the tree.  Finished is a property we use to short-circuit
            // vising the rest of the tree.  Note: Returning false only prevents 
            // the children from being visited, we need to not visit siblings either.
            if (!data.Finished && data.LastNodeMatched == dependencyObject)
            {
                data.Finished = true;
            }

            return false;
        }
        /// <summary>
        ///     Resolves a single locator starting at the given startNode.  
        /// Sets the selection and attachmentLevel if necessary.
        /// </summary>
        /// <param name="selection">object representing the content that has been resolved 
        /// so far; updated if the locator passed in is resolved</param>
        /// <param name="attachmentLevel">attachmentLevel of content that has been resolved 
        /// so far; updated based on the resolution of the passed in locator</param>
        /// <param name="attemptedLevel">the level that is represented by this locator - 
        /// start, middle or end</param>
        /// <param name="locator">the locator to resolve</param>
        /// <param name="offset">the offset into the locator to start the resolution at</param>
        /// <param name="startNode">the node to start the resolution at</param>
        /// <param name="skipStartNode">whether or not the start node should be looked at</param> 
        /// <returns>the data representing the resolution of the single locator; used for 
        /// special cases by calling code to override results from this method</returns>
        private ResolvingLocatorState ResolveSingleLocator(ref object selection, ref AttachmentLevel attachmentLevel, AttachmentLevel attemptedLevel, ContentLocator locator, int offset, DependencyObject startNode, bool skipStartNode)
        {
            ResolvingLocatorState data = new ResolvingLocatorState();
            data.LocatorPartIndex = offset;
            data.ContentLocatorBase = locator;

            PrePostDescendentsWalker<ResolvingLocatorState> walker = new PrePostDescendentsWalker<ResolvingLocatorState>(TreeWalkPriority.VisualTree, ResolveLocatorPart, TerminateResolve, data);
            walker.StartWalk(startNode, skipStartNode);

            if (data.AttachmentLevel == AttachmentLevel.Full && data.AttachedAnchor != null)
            {
                // Merge the results with pre-existing selection
                if (selection != null)
                {
                    SelectionProcessor selProcessor = GetSelectionProcessor(selection.GetType());
                    object newSelection;
                    if (selProcessor != null)
                    {
                        if (selProcessor.MergeSelections(selection, data.AttachedAnchor, out newSelection))
                        {
                            selection = newSelection;
                        }
                        else
                        {
                            // If we can't merge, them this locator isn't included in final results so we
                            // we turn off the level that we are attempting to resolve
                            attachmentLevel &= ~attemptedLevel;
                        }
                    }
                    else
                    {
                        // If not selection processor, the locator can't be resolved so 
                        // we turn off the level that we were attempting to resolve
                        attachmentLevel &= ~attemptedLevel;
                    }
                }
                else
                {
                    selection = data.AttachedAnchor;
                }
            }
            else
            {
                // ContentLocator didn't fully resolve so we turn off the level
                // that we were attempting to resolve
                attachmentLevel &= ~attemptedLevel;
            }

            return data;
        }