//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods #region Generating Locators /// <summary> /// Walks the path through the element tree specified by 'root' and /// produces a set of locators identifying the path based on /// DependencyProperty settings on the tree. /// </summary> /// <param name="processor">SubTreeProcessor to use on node</param> /// <param name="startNode">the PathNode identifying the root of the subtree to process</param> /// <param name="selection">selection for which Locators are being generated</param> /// <returns> /// a set of locators identifying the path through the element tree; /// can return null if no locators can be generated for this root /// </returns> /// <exception cref="ArgumentNullException">root is null</exception> /// <exception cref="SystemException">no AnnotationStore is available from /// the element tree</exception> private IList<ContentLocatorBase> GenerateLocators(SubTreeProcessor processor, PathNode startNode, Object selection) { Debug.Assert(startNode != null, "startNode can not be null"); List <ContentLocatorBase> locatorsToReturn = new List<ContentLocatorBase> (); bool continueProcessing = true; // Process the current PathNode, with the possibilty of doing the whole // subtree (in which case continueProcessing comes back false). ContentLocator list = processor.GenerateLocator(startNode, out continueProcessing); bool processSelection = list != null; IList<ContentLocatorBase> newLocators = null; // If we should continue processing, we look at the children of the // root. Depending on the number of children we do different things. if (continueProcessing) { switch (startNode.Children.Count) { case 0 : // No children - we just return what we have so far if (list != null) { locatorsToReturn.Add(list); } break; case 1 : // One child - we ask the root of the subtree for the processor // to use and then ask the processor to handle the subtree. SubTreeProcessor newProcessor = GetSubTreeProcessor(startNode.Node); newLocators = GenerateLocators(newProcessor, (PathNode)startNode.Children[0], selection); if (newLocators != null && newLocators.Count > 0) processSelection = false; if (list != null) locatorsToReturn.AddRange(Merge(list, newLocators)); else locatorsToReturn.AddRange(newLocators); break; default : // Multiple children - we must process all the children as a // locator set. This returns one or more locators that all start // start with a ContentLocatorGroup which can have one locator for each // child. ContentLocatorBase newLocator = GenerateLocatorGroup(startNode, selection); if (newLocator != null) processSelection = false; if (list != null) locatorsToReturn.Add(list.Merge(newLocator)); else if (newLocator != null) locatorsToReturn.Add(newLocator); break; } } else { // If we shouldn't continue processing we package up the // locator we got from the first GenerateLocator call if (list != null) { locatorsToReturn.Add(list); } } // If we produced a locator for root and no one below us did as well, // we need to process the selection, if any if (processSelection && selection != null) { SelectionProcessor selProcessor = GetSelectionProcessor(selection.GetType()); if (selProcessor != null) { IList <ContentLocatorPart> locatorParts = selProcessor.GenerateLocatorParts(selection, startNode.Node); // Possible bug - AddLocatorPartsToLocator was only written to handle normal locators, not // locator groups. ToDo if (locatorParts != null && locatorParts.Count > 0) { List<ContentLocatorBase> tempLocators = new List<ContentLocatorBase>(locatorsToReturn.Count * locatorParts.Count); foreach (ContentLocatorBase loc in locatorsToReturn) { tempLocators.AddRange(((ContentLocator)loc).DotProduct(locatorParts)); // } locatorsToReturn = tempLocators; } } } return locatorsToReturn; }
/// <summary> /// Produces a set of locators that uses locator groups to /// span multiple branches in the tree. /// </summary> /// <param name="node">the PathNode identifying the root of the subtree to process</param> /// <param name="selection">the selection we are currently processing</param> /// <returns> /// a locator set containing locators identifying the path /// through the element tree /// </returns> /// <exception cref="ArgumentNullException">node is null</exception> /// <exception cref="SystemException">no AnnotationStore is available from /// the element tree</exception> private ContentLocatorBase GenerateLocatorGroup(PathNode node, Object selection) { Debug.Assert(node != null, "node can not be null"); SubTreeProcessor processor = GetSubTreeProcessor(node.Node); IList<ContentLocatorBase> tempLocators = null; ContentLocatorGroup ContentLocatorGroup = new ContentLocatorGroup(); // Produce a locator representing each child and add it // to the locator set. NOTE - We currently only support one // locator per branch. foreach (PathNode child in node.Children) { tempLocators = GenerateLocators(processor, child, selection); if (tempLocators != null && tempLocators.Count > 0) { // Don't add empty Locators to the ContentLocatorGroup if (tempLocators[0] != null) { ContentLocatorGroup locatorGroup = null; ContentLocator locator = tempLocators[0] as ContentLocator; if (locator != null && locator.Parts.Count != 0) { // NOTE - We currently only support producing one locator // per branch of the locator set. ContentLocatorGroup.Locators.Add(locator); } else if ((locatorGroup = tempLocators[0] as ContentLocatorGroup) != null) { // } } } } // If the locators set is empty, we return null // If only one locator was generated for the children, we return that // locator directly. No need for a ContentLocatorGroup // Otherwise return the ContentLocatorGroup if (ContentLocatorGroup.Locators.Count == 0) { return null; } else if (ContentLocatorGroup.Locators.Count == 1) { ContentLocator list = ContentLocatorGroup.Locators[0]; ContentLocatorGroup.Locators.Remove(list); return list; } else { return ContentLocatorGroup; } }
/// <summary> /// Generates a locator part list identifying node. If node has a /// value for DataIdProperty, a locator with a single locator part /// containing the id value is returned. Otherwise null is returned. /// </summary> /// <param name="node">the node to generate a locator for</param> /// <param name="continueGenerating">specifies whether or not generating should /// continue for the rest of the path; always set to true</param> /// <returns>if node has a value for DataIdProperty, a locator with a /// single locator part containing the id value; null otherwise /// </returns> /// <exception cref="ArgumentNullException">node is null</exception> public override ContentLocator GenerateLocator(PathNode node, out bool continueGenerating) { if (node == null) throw new ArgumentNullException("node"); continueGenerating = true; ContentLocator locator = null; ContentLocatorPart newLocatorPart = CreateLocatorPart(node.Node); if (newLocatorPart != null) { locator = new ContentLocator(); locator.Parts.Add(newLocatorPart); } return locator; }
/// <summary> /// Generates locators identifying 'chunks'. If node is a chunk, /// generates a locator with a single locator part containing a /// fingerprint of the chunk. Otherwise null is returned. /// </summary> /// <param name="node">the node to generate a locator for</param> /// <param name="continueGenerating">return flag indicating whether the search /// should continue (presumably because the search was not exhaustive). /// This processor will always return true because it is possible to locate /// parts of the node (if it is FixedPage or FixedPageProxy)</param> /// <returns>if node is a FixedPage or FixedPageProxy, a ContentLocator /// with a single locator part containing the page number; null if node is not /// FixedPage or FixedPageProxy </returns> /// <exception cref="ArgumentNullException">node is null</exception> /// <exception cref="ArgumentException">node points to a Document Page View which /// doesn't contain a FixedDocumentPage</exception> public override ContentLocator GenerateLocator(PathNode node, out bool continueGenerating) { if (node == null) throw new ArgumentNullException("node"); // Initial value continueGenerating = true; ContentLocator locator = null; DocumentPageView dpv = node.Node as DocumentPageView; int pageNb = -1; if (dpv != null) { // Only produce locator parts for FixedDocumentPages if (dpv.DocumentPage is FixedDocumentPage || dpv.DocumentPage is FixedDocumentSequenceDocumentPage) { pageNb = dpv.PageNumber; } } else { FixedTextSelectionProcessor.FixedPageProxy fPage = node.Node as FixedTextSelectionProcessor.FixedPageProxy; if (fPage != null) { pageNb = fPage.Page; } } if (pageNb >= 0) { locator = new ContentLocator(); ContentLocatorPart locatorPart = CreateLocatorPart(pageNb); locator.Parts.Add(locatorPart); } return locator; }
/// <summary> /// Generates a locator part list identifying node. /// </summary> /// <remarks> /// Most subclasses will simply return a ContentLocator with one locator /// part in it. In some cases, more than one locator part may be /// required (e.g., a node which represents path to some data may /// need a separate locator part for each portion of the path). /// </remarks> /// <param name="node">the node to generate a locator for</param> /// <param name="continueGenerating">specifies whether or not generating should /// continue for the rest of the path; a SubTreeProcessor could return false if /// it processed the rest of the path itself </param> /// <returns> /// a locator identifying 'node'; in most cases this locator will /// only contain one locator part; can return null if no /// locator part can be generated for the given node /// </returns> /// <exception cref="ArgumentNullException">node is null</exception> public abstract ContentLocator GenerateLocator(PathNode node, out bool continueGenerating);
/// <summary> /// Adds a branch to an existing path, removing any duplicate /// nodes as necessary. Assumes that both paths are full paths /// up to the root of the same tree. If the paths are not full, /// the method will give incorrect results. If the paths are /// full but belong to different trees (and therefore have /// different roots) the method will throw. /// </summary> /// <param name="path">path to add branch to</param> /// <param name="branch">branch to be added; should be a linear path /// (no more than one child for any node)</param> /// <returns>the path with branch having been added in and duplicate /// nodes pruned</returns> private static PathNode AddBranchToPath(PathNode path, PathNode branch) { Debug.Assert(path != null, "path can not be null"); Debug.Assert(branch != null, "branch can not be null"); // The paths must be in the same tree and therefore have the // same root. Debug.Assert(path.Node.Equals(branch.Node), "path.Node is not equal to branch.Node"); PathNode fp = path; PathNode sp = branch; // Continue down while (fp.Node.Equals(sp.Node) && sp._children.Count > 0) { // if the firstpath component equals the second path component // then we try to find the second path child component // inside the first path children bool found = false; PathNode branchNode = (PathNode)sp._children[0]; foreach (PathNode fpn in fp._children) { if (fpn.Equals(branchNode)) { // if we found one we keep moving along both the first path and the second path found = true; sp = branchNode; fp = fpn; break; } } if (found) continue; // if we can not find the second path child in the first // path child, we then just add the second path child // to the set of first path children fp.AddChild(branchNode); break; } return path; }
/// <summary> /// Builds a path from an element to the root of its tree. Every /// element in between the element and the root is added to the /// path. /// </summary> /// <param name="node">the element to build a path for</param> /// <returns>the PathNode instance referring to the root of the tree; its /// children/descendants only include the nodes between the root and /// node</returns> private static PathNode BuildPathForElement(DependencyObject node) { Debug.Assert(node != null, "node can not be null"); PathNode childNode = null; while (node != null) { PathNode pathNode = new PathNode(node); if (childNode != null) pathNode.AddChild(childNode); childNode = pathNode; // If we find a node that has the service set on it, we should stop // after processing it. For cases without a service like unit tests, // this node won't be found and we'll continue to the root. if (node.ReadLocalValue(AnnotationService.ServiceProperty) != DependencyProperty.UnsetValue) break; node = PathNode.GetParent(node); } return childNode; }