Code:
/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / MS / Internal / Annotations / Anchoring / LocatorManager.cs / 1 / LocatorManager.cs
#pragma warning disable 1634, 1691 //------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // The main entry-point to the Anchoring namespace. LocatorManager is the // controller for the Anchoring algorithms. Most of the work is delegated // to processors. LocatorManager maintains a registry of processors. // Spec: [....]/sites/ag/Specifications/Anchoring%20Namespace%20Spec.doc // // History: // 12/01/2002: [....]: Created - based on architectural discussions and design // by [....], [....], [....] // 02/01/2003: [....]: Added code for generating locators // 07/21/2003: [....]: Ported to WCP // 08/18/2003: [....]: Updated to Anchoring Namespace spec. // 02/10/2004: [....]: Made it DispatcherObject // //----------------------------------------------------------------------------- using System; using System.IO; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Windows; using System.Windows.Annotations; using System.Windows.Annotations.Storage; using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Documents; using System.Windows.Markup; using System.Windows.Threading; using System.Xml; using MS.Internal; using MS.Utility; namespace MS.Internal.Annotations.Anchoring { ////// The main entry-point to the Anchoring namespace. LocatorManager is the /// controller for the Anchoring algorithms. Most of the work is delegated /// to processors. LocatorManager maintains a registry of processors. /// sealed internal class LocatorManager : DispatcherObject { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors ////// Create an instance of LocatorManager. It manages the /// processors that are used to create and resolve locators. /// public LocatorManager() : this(null) { } ////// Create an instance of LocatorManager with an optional specific store. /// It manages the processors that are used to create and resolve locators. /// If a store is passed in, its used to query for annotations. Otherwise /// the service is looked for and the service's store is used. /// /// optional, store to use for query of annotations public LocatorManager(AnnotationStore store) { _locatorPartHandlers = new Hashtable(); _subtreeProcessors = new Hashtable(); _selectionProcessors = new Hashtable(); RegisterSubTreeProcessor(new DataIdProcessor(this), DataIdProcessor.Id); RegisterSubTreeProcessor(new FixedPageProcessor(this), FixedPageProcessor.Id); TreeNodeSelectionProcessor nodeProcessor = new TreeNodeSelectionProcessor(); RegisterSelectionProcessor(nodeProcessor, typeof(FrameworkElement)); RegisterSelectionProcessor(nodeProcessor, typeof(FrameworkContentElement)); TextSelectionProcessor textProcessor = new TextSelectionProcessor(); RegisterSelectionProcessor(textProcessor, typeof(TextRange)); RegisterSelectionProcessor(textProcessor, typeof(TextAnchor)); _internalStore = store; } #endregion Constructors //------------------------------------------------------ // // Public Methods // //----------------------------------------------------- #region Public Methods #region Processor Caches ////// Registers a subtree processor. The processorId is the string to be /// used as the value of the SubTreeProcessorIdProperty for this processor. /// This call overrides any previous registrations for processorId or for /// the locator part types recognized by processor. /// /// instance to be registered /// string id used to specify this processor as /// the SubTreeProcessorIdProperty value ///if the processor or processorId are null public void RegisterSubTreeProcessor(SubTreeProcessor processor, String processorId) { VerifyAccess(); if (processor == null) throw new ArgumentNullException("processor"); if (processorId == null) throw new ArgumentNullException("processorId"); XmlQualifiedName[] locatorPartTypes = processor.GetLocatorPartTypes(); _subtreeProcessors[processorId] = processor; if (locatorPartTypes != null) { foreach (XmlQualifiedName typeName in locatorPartTypes) { _locatorPartHandlers[typeName] = processor; } } } ////// Returns the subtree processor set on this tree node to be used to /// process the subtree rooted at this node. The subtree processor /// returned is not the one used to process this node - only its /// subtree. If no subtree processor is specified on this node /// or any of its ancestors in the tree, an instance of the DataIdProcessor /// is returned. /// /// tree node we are retrieving subtree processor for ///a subtree processor specified by the SubTreeProcessorIdProperty /// on this node, or an instance of the DataIdProcessor if none is specified ///node is null public SubTreeProcessor GetSubTreeProcessor(DependencyObject node) { VerifyAccess(); if (node == null) throw new ArgumentNullException("node"); // This property should contain one or more (comma-delimited) // registered string IDs for subtree processors. string processorString = node.GetValue(SubTreeProcessorIdProperty) as string; if (!String.IsNullOrEmpty(processorString)) { SubTreeProcessor processor = (SubTreeProcessor)_subtreeProcessors[processorString]; if (processor != null) return processor; else throw new ArgumentException(SR.Get(SRID.InvalidSubTreeProcessor, processorString)); } else { return _subtreeProcessors[DataIdProcessor.Id] as SubTreeProcessor; } } ////// Returns the subtree processor registered to handle the locator /// part's type. If none is registered, null is returned. /// /// locator part for which a subtree processor /// is being retreived ///a subtree processor registered to handle the locator part's /// type; null if no such processor is registred ///locatorPart is null public SubTreeProcessor GetSubTreeProcessorForLocatorPart(ContentLocatorPart locatorPart) { VerifyAccess(); if (locatorPart == null) throw new ArgumentNullException("locatorPart"); return _locatorPartHandlers[locatorPart.PartType] as SubTreeProcessor; } ////// Registers a selection processor for a selection Type. /// This call overrides any previous registrations for the Type. /// The processor also provides an array of locator part types it knows how /// to handle. If a processor provides a locator part type that has been /// provided by a previously registered processor, the last processor to be /// registered overrides all others. /// ////// GetSelectionProcessor(Type) looks for a processor registered for /// the Type or any of its base Types. Interfaces are not taken into /// account. Do not register a selection processor for an interface /// Type. /// /// instance to be registered /// Type of selection processed by this processor ///processor or selectionType is null public void RegisterSelectionProcessor(SelectionProcessor processor, Type selectionType) { VerifyAccess(); if (processor == null) throw new ArgumentNullException("processor"); if (selectionType == null) throw new ArgumentNullException("selectionType"); XmlQualifiedName[] locatorPartTypes = processor.GetLocatorPartTypes(); _selectionProcessors[selectionType] = processor; if (locatorPartTypes != null) { foreach (XmlQualifiedName type in locatorPartTypes) { _locatorPartHandlers[type] = processor; } } } ////// Returns the selection processor for selections of the specified /// Type. If no processor is registered for the specified Type, the /// Type's base Type (as defined by Type.BaseType) is checked, and so /// on. Interfaces implemented by the Type or any of its base Types /// are not taken into account. If no processor is registered for Type /// or any of its base Types, null is returned. /// /// the Type of selection for which a handler /// is requested ///the selection processor for the specified Type or one of its /// base Types; null if no processor has been registered for this Type or /// any of its base Types ///selectionType is null public SelectionProcessor GetSelectionProcessor(Type selectionType) { VerifyAccess(); if (selectionType == null) throw new ArgumentNullException("selectionType"); SelectionProcessor processor = null; // We keep looking until we find a processor or // there are no more base types to check for do { processor = _selectionProcessors[selectionType] as SelectionProcessor; selectionType = selectionType.BaseType; } while (processor == null && selectionType != null); return processor; } ////// Returns the selection processor registered to handle the locator /// part's type. If none is registered, null is returned. /// /// locator part for which a selection processor is /// being retreived ///the selection processor for the locatorPart's type; null if no /// processor has been registerd for that type ///locatorPart is null public SelectionProcessor GetSelectionProcessorForLocatorPart(ContentLocatorPart locatorPart) { VerifyAccess(); if (locatorPart == null) throw new ArgumentNullException("locatorPart"); return _locatorPartHandlers[locatorPart.PartType] as SelectionProcessor; } #endregion Processor Caches ////// Called by processors when they've encountered content in the tree /// that should have annotations processed for it. This is a low-level /// method that will get called several times during the algorithm for /// loading annotations. /// /// the tree node that needs to be processed ///list of IAttachedAnnotations that were loaded for 'node'; /// the list will never be null but may be empty ///node is null ///no AnnotationStore is available from /// the element tree public IListProcessAnnotations(DependencyObject node) { VerifyAccess(); if (node == null) throw new ArgumentNullException("node"); IList attachedAnnotations = new List (); IList locators = GenerateLocators(node); if(locators.Count > 0) { AnnotationStore store = null; if (_internalStore != null) { store = _internalStore; } else { AnnotationService service = AnnotationService.GetService(node); if (service == null || !service.IsEnabled) { throw new InvalidOperationException(SR.Get(SRID.AnnotationServiceNotEnabled)); } store = service.Store; } // LocatorBases for a single node should always be Locators ContentLocator[] lists = new ContentLocator[locators.Count]; locators.CopyTo(lists, 0); IList annotations = store.GetAnnotations(lists[0]); foreach (ContentLocator locator in locators) { if (locator.Parts[locator.Parts.Count - 1].NameValuePairs.ContainsKey(TextSelectionProcessor.IncludeOverlaps)) { locator.Parts.RemoveAt(locator.Parts.Count - 1); } } foreach (Annotation annotation in annotations) { foreach (AnnotationResource anchor in annotation.Anchors) { foreach(ContentLocatorBase locator in anchor.ContentLocators) { AttachmentLevel attachmentLevel; object attachedAnchor = FindAttachedAnchor(node, lists, locator, out attachmentLevel); if(attachmentLevel != AttachmentLevel.Unresolved) { Debug.Assert(attachedAnchor != null, "AttachedAnchor cannot be null if attachmentLevel is not Unresolved."); attachedAnnotations.Add(new AttachedAnnotation(this, annotation, anchor, attachedAnchor, attachmentLevel)); // Only process one locator per resource break; } } } } } return attachedAnnotations; } /// /// Generates zero or more Locators that map to the passed in /// anchor. The Locators are generated using the /// SubTreeProcessorIdProperty values set on the tree containing /// the anchor and the processors registered. /// /// the anchor to generate Locators for ///an array of Locators; will never return null but may return /// an empty list ///if selection is null ///if no processor is registered for /// selection's Type public IListGenerateLocators(Object selection) { VerifyAccess(); if (selection == null) throw new ArgumentNullException("selection"); ICollection nodes = null; SelectionProcessor selProcessor = GetSelectionProcessor(selection.GetType()); if (selProcessor != null) { nodes = (ICollection)selProcessor.GetSelectedNodes(selection); } else { throw new ArgumentException("Unsupported Selection", "selection"); } IList returnLocators = null; PathNode pathRoot = PathNode.BuildPathForElements(nodes); if (pathRoot != null) { SubTreeProcessor processor = GetSubTreeProcessor(pathRoot.Node); Debug.Assert(processor != null, "SubtreeProcessor can not be null"); returnLocators = GenerateLocators(processor, pathRoot, selection); } // We never return null. A misbehaved processor might return null so we fix it up. if (returnLocators == null) returnLocators = new List (0); return returnLocators; } /// /// Produces an anchor spanning the content specified by 'locator'. /// This method traverses the tree and, using the registered /// processors, resolves the locator in the current element tree. /// /// the locator to be resolved /// the index of the locator part to begin resolution with, ignored for /// LocatorGroups which always start with the first locator part /// the tree node to start the resolution from /// type of the returned anchor ///an anchor that spans the content specified by locator; may return /// null if the locator could not be resolved (in which case type is set to /// AttachmentLevel.Unresolved ///locator or startNode are null ///offset is negative or greater than /// locator.Count - 1 public Object ResolveLocator(ContentLocatorBase locator, int offset, DependencyObject startNode, out AttachmentLevel attachmentLevel) { VerifyAccess(); if (locator == null) throw new ArgumentNullException("locator"); if (startNode == null) throw new ArgumentNullException("startNode"); // Offset need only be checked for Locators ContentLocator realLocator = locator as ContentLocator; if (realLocator != null) { if (offset < 0 || offset >= realLocator.Parts.Count) throw new ArgumentOutOfRangeException("offset"); } return InternalResolveLocator(locator, offset, startNode, false /*skipStartNode*/, out attachmentLevel); } #endregion Public Methods //------------------------------------------------------ // // Public Operators // //------------------------------------------------------ //----------------------------------------------------- // // Public Events // //------------------------------------------------------ //----------------------------------------------------- // // Public Properties // //----------------------------------------------------- #region Public Properties ////// DependencyProperty used to specify the processor to use for a /// given subtree. When set on a node, all nodes below it will be /// processed with the specified processor, unless overriden. Setting /// this property after annotations have been loaded will have no effect /// on existing annotations. If you want to change how the tree is /// processed, you should set this property and call LoadAnnotations/ /// UnloadAnnotations on the service. /// #pragma warning suppress 7009 public static readonly DependencyProperty SubTreeProcessorIdProperty = DependencyProperty.RegisterAttached( "SubTreeProcessorId", typeof(string), typeof(LocatorManager), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.Inherits | FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior)); ////// Sets the value of the SubTreeProcessorId attached property /// of the LocatorManager class. /// /// element to which to write the attached property /// the value to set ///d is null public static void SetSubTreeProcessorId(DependencyObject d, String id) { if(d == null) throw new ArgumentNullException("d"); //d will check the context d.SetValue(SubTreeProcessorIdProperty, id); } ////// Gets the value of the SubTreeProcessorId attached property /// of the LocatorManager class. /// /// the object from which to read the attached property ///the value of the SubTreeProcessorId attached property ///d is null [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public static String GetSubTreeProcessorId(DependencyObject d) { if (d == null) throw new ArgumentNullException("d"); //d will check the context return d.GetValue(SubTreeProcessorIdProperty) as String; } #endregion Public Properties //----------------------------------------------------- // // Internal Methods // //------------------------------------------------------ #region Internal Methods ////// Traverse the element tree starting at subtree and load /// annotations for that subtree. /// /// root of the subtree for which to load annotations ///list of IAttachedAnnotations that were loaded for 'node'; /// the list will never be null but may be empty ///subtree is null internal IListProcessSubTree(DependencyObject subTree) { if (subTree == null) throw new ArgumentNullException("subTree"); ProcessingTreeState data = new ProcessingTreeState(); PrePostDescendentsWalker walker = new PrePostDescendentsWalker (TreeWalkPriority.VisualTree, PreVisit, PostVisit, data); walker.StartWalk(subTree); return data.AttachedAnnotations; } /// /// Searches the element subtree for a node that maps to the /// passed in ContentLocatorBase. /// Note: For LocatorGroups the startNode, offset, and prefixes /// are ignored. Due to the nature of LocatorGroups, we always /// start searching at the node with the service enabled, with /// the first locator part, ignoring all prefixes. /// /// the root of the subtree to search /// locators for the root of the subtree /// the ContentLocatorBase we are resolving to an anchor /// type of the anchor returned ///the anchor for the passed in ContentLocatorBase; will return null if no match can be found ///startNode or locator is null internal Object FindAttachedAnchor(DependencyObject startNode, ContentLocator[] prefixes, ContentLocatorBase locator, out AttachmentLevel attachmentLevel) { if (startNode == null) throw new ArgumentNullException("startNode"); if (locator == null) throw new ArgumentNullException("locator"); // Set it to unresolved initially attachmentLevel = AttachmentLevel.Unresolved; Object anchor = null; bool matched = true; int locatorPartIdx = FindMatchingPrefix(prefixes, locator, out matched); // The annotation's locator starts with at least // one of the locators for the local root. if (matched) { ContentLocator realLocator = locator as ContentLocator; if (realLocator == null || locatorPartIdx < realLocator.Parts.Count) { // Now we try to resolve. If any locator parts were matched to the startNode we want to // start resolving with its children, skipping a revisit to the startNode. anchor = InternalResolveLocator(locator, locatorPartIdx, startNode, locatorPartIdx != 0 /*skipStartNode*/, out attachmentLevel); } // If nothing was returned, we base our return values on the results // of matching against the local root. if (attachmentLevel == AttachmentLevel.Unresolved && locatorPartIdx > 0) { if (locatorPartIdx == 0) { attachmentLevel = AttachmentLevel.Unresolved; } // If there was anything left to resolve then its incomplete // else if (realLocator != null && locatorPartIdx < realLocator.Parts.Count) { attachmentLevel = AttachmentLevel.Incomplete; anchor = startNode; } // otherwise its fully resolved else { attachmentLevel = AttachmentLevel.Full; anchor = startNode; } } } return anchor; } ////// Determines if the locator matches any of the prefixes, and if so, returns /// the length of the prefix that was matched. Resolving of the locator can /// begin with the first locator part after those that were matched by the prefix. /// /// locators representing prefixes to match /// locator to find a match for /// whether or not a match was found ///index of the next locator part to resolve private int FindMatchingPrefix(ContentLocator[] prefixes, ContentLocatorBase locator, out bool matched) { matched = true; int locatorPartIdx = 0; ContentLocator realLocator = locator as ContentLocator; // If we have a locator set or there are no prefixes then we // are 'matched' implicitly. if (realLocator != null && prefixes != null && prefixes.Length > 0) { matched = false; foreach (ContentLocator prefix in prefixes) { if (realLocator.StartsWith(prefix)) { locatorPartIdx = prefix.Parts.Count; matched = true; break; } } } return locatorPartIdx; } #endregion Internal Methods //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods #region Generating Locators ////// 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. /// /// SubTreeProcessor to use on node /// the PathNode identifying the root of the subtree to process /// selection for which Locators are being generated ////// a set of locators identifying the path through the element tree; /// can return null if no locators can be generated for this root /// ///root is null ///no AnnotationStore is available from /// the element tree private IListGenerateLocators(SubTreeProcessor processor, PathNode startNode, Object selection) { Debug.Assert(startNode != null, "startNode can not be null"); List locatorsToReturn = new List (); 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 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 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 tempLocators = new List (locatorsToReturn.Count * locatorParts.Count); foreach (ContentLocatorBase loc in locatorsToReturn) { tempLocators.AddRange(((ContentLocator)loc).DotProduct(locatorParts)); // } locatorsToReturn = tempLocators; } } } return locatorsToReturn; } /// /// Produces a set of locators that uses locator groups to /// span multiple branches in the tree. /// /// the PathNode identifying the root of the subtree to process /// the selection we are currently processing ////// a locator set containing locators identifying the path /// through the element tree /// ///node is null ///no AnnotationStore is available from /// the element tree private ContentLocatorBase GenerateLocatorGroup(PathNode node, Object selection) { Debug.Assert(node != null, "node can not be null"); SubTreeProcessor processor = GetSubTreeProcessor(node.Node); IListtempLocators = 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; } } #endregion Generating Locators #region Processing Tree /// /// Callback from DescendentsWalker before the nodes children are visited. /// /// node to visit /// data used for the current tree walk ///returns whether or not the children should be visited private bool PreVisit(DependencyObject dependencyObject, ProcessingTreeState data) { Debug.Assert(data != null, "dataBlob is either null or not of ProcessingTreeState type"); bool calledProcessAnnotations = false; SubTreeProcessor processor = GetSubTreeProcessor(dependencyObject); Debug.Assert(processor != null, "SubtreeProcessor can not be null"); // There is always a default processor IListattachedAnnotations = processor.PreProcessNode(dependencyObject, out calledProcessAnnotations); if (attachedAnnotations != null) data.AttachedAnnotations.AddRange(attachedAnnotations); // Combine whether this processor called ProcessAnnotations with the info of its siblings data.CalledProcessAnnotations = data.CalledProcessAnnotations || calledProcessAnnotations; data.Push(); // Returning true here prevents children from being called return !calledProcessAnnotations; } /// /// Callback from DescendentsWalker after the nodes children are visited. /// /// node to visit /// data used for the current tree walk ///return value is ignored private bool PostVisit(DependencyObject dependencyObject, ProcessingTreeState data) { Debug.Assert(data != null, "dataBlob is either null or not of ProcessingTreeState type"); bool childrenCalledProcessAnnotations = data.Pop(); SubTreeProcessor processor = GetSubTreeProcessor(dependencyObject); Debug.Assert(processor != null, "SubtreeProcessor can not be null"); bool calledProcessAnnotations = false; IListattachedAnnotations = processor.PostProcessNode(dependencyObject, childrenCalledProcessAnnotations, out calledProcessAnnotations); if (attachedAnnotations != null) data.AttachedAnnotations.AddRange(attachedAnnotations); // This flag is information available to PostVisit calls data.CalledProcessAnnotations = data.CalledProcessAnnotations || calledProcessAnnotations || childrenCalledProcessAnnotations; // This return value is not used by PrePostDescendentsWalker return true; } #endregion Processing Tree #region Resolving Locators /// /// Resolves a locator starting on a specified locator part with a specified tree node. /// The tree node can optionally be skipped (if some previous locator part has already /// matched it) in which case the resolution starts with its children. /// /// the locator to resolve /// the index of the first locator part to resolve /// the node to start the resolution at /// specifies whether to start with the startNode or its children /// return value specifying how successful the resolution was ///the attached anchor the locator was resolved to ///if a locator set is resolved and the individual selections /// can't be merged private Object InternalResolveLocator(ContentLocatorBase locator, int offset, DependencyObject startNode, bool skipStartNode, out AttachmentLevel attachmentLevel) { Debug.Assert(locator != null, "locator can not be null"); Debug.Assert(startNode != null, "startNode can not be null"); // Set it to unresolved initially attachmentLevel = AttachmentLevel.Full; Object selection = null; ContentLocatorGroup locatorGroup = locator as ContentLocatorGroup; ContentLocator realLocator = locator as ContentLocator; AttachmentLevel individualAttachmentLevel = AttachmentLevel.Unresolved; // If only one locator part left, it might represent a selection so we take // care of that case before trying to resolve the locator part if (realLocator != null && offset == realLocator.Parts.Count - 1) { ContentLocatorPart locatorPart = realLocator.Parts[offset]; SelectionProcessor selProcessor = GetSelectionProcessorForLocatorPart(locatorPart); if (selProcessor != null) { selection = selProcessor.ResolveLocatorPart(locatorPart, startNode, out individualAttachmentLevel); attachmentLevel = individualAttachmentLevel; // No node has actually been matched in this case so we // return the default Unresolved (set at top of method). // Its up to the caller to know if the node and // index passed in represented an incomplete resolution. return selection; } } IListlocators = null; // Setup the locators and other inputs before the loop. Normal locators // are put in an array of locators (with one element). LocatorGroups have // their Locators collection used. if (locatorGroup == null) { Debug.Assert(offset >= 0 && offset < realLocator.Parts.Count, "offset out of range"); locators = new List (1); locators.Add(realLocator); } else { // If there is a service, start at its root. AnnotationService svc = AnnotationService.GetService(startNode); if (svc != null) { startNode = svc.Root; } locators = locatorGroup.Locators; // Always start resolving locator groups from the beginning // and use the node the service is enabled on to start offset = 0; skipStartNode = false; } bool middlePortionExists = true; if (locators.Count > 0) { // Otherwise we need to resolve each of the locators in the locator set // and then try to merge the anchors that are returned ResolvingLocatorState data = ResolveSingleLocator(ref selection, ref attachmentLevel, AttachmentLevel.StartPortion, locators[0], offset, startNode, skipStartNode); // Special case - when there is only one locator we simply use the anchor and level // returned from resolving that single locator if (locators.Count == 1) { selection = data.AttachedAnchor; attachmentLevel = data.AttachmentLevel; } else { // Resolve all locators after the first and before the last if (locators.Count > 2) { AttachmentLevel tempLevel = AttachmentLevel.Unresolved; AttachmentLevel savedLevel = attachmentLevel; for (int i = 1; i < locators.Count - 1; i++) { data = ResolveSingleLocator(ref selection, ref attachmentLevel, AttachmentLevel.MiddlePortion, locators[i], offset, startNode, skipStartNode); //if there are >1 middle locators some of them might be resolved, some other - not //if even one middle locator is resolved we should save its attachmenLevel if ((tempLevel == AttachmentLevel.Unresolved) || ((attachmentLevel & AttachmentLevel.MiddlePortion) != 0)) tempLevel = attachmentLevel; attachmentLevel = savedLevel; } attachmentLevel = tempLevel; } else { // We make note that there were no middle portion locators middlePortionExists = false; } // Process the last locator data = ResolveSingleLocator(ref selection, ref attachmentLevel, AttachmentLevel.EndPortion, locators[locators.Count - 1], offset, startNode, skipStartNode); // If no locators exists for the middle portion we need to make // sure its not the only portion left on. if (!middlePortionExists && attachmentLevel == AttachmentLevel.MiddlePortion) { attachmentLevel &= ~AttachmentLevel.MiddlePortion; } //if start and end is resolved we consider this as fully resolved //this will handle the case of empty middle page in fixed if (attachmentLevel == (AttachmentLevel.StartPortion | AttachmentLevel.EndPortion)) attachmentLevel = AttachmentLevel.Full; } } else { // There are no locators to resolve attachmentLevel = AttachmentLevel.Unresolved; } return selection; } /// /// Resolves a single locator starting at the given startNode. /// Sets the selection and attachmentLevel if necessary. /// /// object representing the content that has been resolved /// so far; updated if the locator passed in is resolved /// attachmentLevel of content that has been resolved /// so far; updated based on the resolution of the passed in locator /// the level that is represented by this locator - /// start, middle or end /// the locator to resolve /// the offset into the locator to start the resolution at /// the node to start the resolution at /// whether or not the start node should be looked at ///the data representing the resolution of the single locator; used for /// special cases by calling code to override results from this method 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; PrePostDescendentsWalkerwalker = new PrePostDescendentsWalker (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; } /// /// 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. /// /// the current node to visit /// data containing the state of the current resolution ///whether or not the children of this node should be visited private bool ResolveLocatorPart(DependencyObject dependencyObject, ResolvingLocatorState data) { 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; } ////// 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. /// /// /// ///private bool TerminateResolve(DependencyObject dependencyObject, ResolvingLocatorState data) { // 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; } #endregion Resolving Locators #region ContentLocatorBase Operations /// /// Adds the additionalLocators to the end of initialLocator. If /// there are more than one additional locators, clones of /// initialLocator are created. This method may be destructive - /// the locators passed in may be modified. /// /// the locator to append the additional locators to /// array of locators that need to be appended ///list of merged locators private IListMerge(ContentLocatorBase initialLocator, IList additionalLocators) { if (additionalLocators == null || additionalLocators.Count == 0) { List res = new List (1); res.Add(initialLocator); return res; } for (int i = 1; i < additionalLocators.Count; i++) { additionalLocators[i] = ((ContentLocatorBase)initialLocator.Clone()).Merge(additionalLocators[i]); } // Avoid making one too many clones... additionalLocators[0] = initialLocator.Merge(additionalLocators[0]); return additionalLocators; } #endregion ContentLocatorBase Operations #endregion Private Methods //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Classes /// /// Private class used to store state while processing the tree. /// It keeps a list of annotations loaded as well as the state of /// the calledProcessAnnotations flags returned by processors. /// private class ProcessingTreeState { public ProcessingTreeState() { _calledProcessAnnotations.Push(false); } ////// Returns list of attached annotations loaded so far. /// public ListAttachedAnnotations { get { return _attachedAnnotations; } } /// /// Returns whether any node in the current node's subtree (including the /// current node itself) has returned true for calledProcessAnnotations. /// public bool CalledProcessAnnotations { get { return _calledProcessAnnotations.Peek(); } set { if (_calledProcessAnnotations.Peek() != value) { _calledProcessAnnotations.Pop(); _calledProcessAnnotations.Push(value); } } } ////// Pushes another boolean on to the stack of return values /// we are maintaining for each level of children. /// public void Push() { // Push on a fresh bool value _calledProcessAnnotations.Push(false); } ////// Pops one return value off the stack and returns it. /// public bool Pop() { return _calledProcessAnnotations.Pop(); } private List_attachedAnnotations = new List (); private Stack _calledProcessAnnotations = new Stack (); } /// /// Data structure that maintains the state during an operation /// to resolve a locator. We keep the locator and locator part /// index as the inputs. We keep the attachment level and /// attached anchor as the outputs. /// LastNodeMatched and Finished are used to short-circuit the /// resolution process once we've made at least an incomplete /// match (in which case we only want to visit the children of /// the last matched node, not its siblings). /// /// This class is not a struct because we don't want it to be /// a value type. /// private class ResolvingLocatorState { public ContentLocator ContentLocatorBase; public int LocatorPartIndex; public AttachmentLevel AttachmentLevel = AttachmentLevel.Unresolved; public Object AttachedAnchor; public bool Finished; public Object LastNodeMatched; } #endregion Private Classes //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields // Hashtable of locator part handlers - keyed by XmlQualifiedName private Hashtable _locatorPartHandlers; // Hashtable of subtree processors - keyed by processor id private Hashtable _subtreeProcessors; // Hashtable of selection processors - keyed by selection Type private Hashtable _selectionProcessors; // Potential separators for subtree processor class names private static readonly Char[] Separators = new Char[] { ',', ' ', ';' }; // Optional store, used if passed in, otherwise we grab the service's store private AnnotationStore _internalStore = null; #endregion Private Fields } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- XamlParser.cs
- XmlAnyElementAttributes.cs
- CodeCommentStatementCollection.cs
- SymLanguageVendor.cs
- SamlAdvice.cs
- WebServiceMethodData.cs
- SchemaNamespaceManager.cs
- CodeSubDirectoriesCollection.cs
- PropertyManager.cs
- COSERVERINFO.cs
- DES.cs
- GenericIdentity.cs
- EventLogException.cs
- CAGDesigner.cs
- FlagsAttribute.cs
- Attribute.cs
- XmlUtil.cs
- TiffBitmapEncoder.cs
- DetailsViewPageEventArgs.cs
- SessionEndingEventArgs.cs
- HtmlTable.cs
- DocumentReferenceCollection.cs
- FormParameter.cs
- TreeNode.cs
- UnsafeNativeMethodsPenimc.cs
- EventListenerClientSide.cs
- PageEventArgs.cs
- RNGCryptoServiceProvider.cs
- ChannelFactoryBase.cs
- _StreamFramer.cs
- DataListCommandEventArgs.cs
- BoundPropertyEntry.cs
- CapabilitiesState.cs
- SupportingTokenSpecification.cs
- StringReader.cs
- AnimationClockResource.cs
- SerialPort.cs
- ToolStripProgressBar.cs
- StructuredTypeEmitter.cs
- FlowDocumentPage.cs
- GeometryValueSerializer.cs
- OracleDateTime.cs
- NetworkCredential.cs
- ClientRuntimeConfig.cs
- FunctionMappingTranslator.cs
- Parsers.cs
- BlurBitmapEffect.cs
- PropertyRef.cs
- DSASignatureDeformatter.cs
- MailHeaderInfo.cs
- TraceXPathNavigator.cs
- CompoundFileReference.cs
- BinaryCommonClasses.cs
- CodeGroup.cs
- ParameterCollection.cs
- ListViewItem.cs
- TraceSection.cs
- PasswordDeriveBytes.cs
- EncryptedReference.cs
- EFDataModelProvider.cs
- TextEditorSpelling.cs
- TabControlEvent.cs
- BinaryFormatter.cs
- SequenceFullException.cs
- NetStream.cs
- ReturnEventArgs.cs
- HttpResponseHeader.cs
- WorkflowMarkupSerializer.cs
- Point4D.cs
- XmlValidatingReaderImpl.cs
- FileSystemWatcher.cs
- DoubleSumAggregationOperator.cs
- Viewport2DVisual3D.cs
- HttpCacheVaryByContentEncodings.cs
- SynchronizedInputProviderWrapper.cs
- CustomActivityDesigner.cs
- AutomationProperties.cs
- BamlResourceContent.cs
- SemaphoreSlim.cs
- _Rfc2616CacheValidators.cs
- OperationAbortedException.cs
- MemberJoinTreeNode.cs
- DataServiceHost.cs
- StrokeCollection2.cs
- HttpModulesSection.cs
- ResourceExpression.cs
- FormatConvertedBitmap.cs
- streamingZipPartStream.cs
- CodeNamespaceImportCollection.cs
- FixedPosition.cs
- BamlResourceDeserializer.cs
- CultureSpecificStringDictionary.cs
- AuthorizationRuleCollection.cs
- CollectionView.cs
- BindingExpressionUncommonField.cs
- Range.cs
- ErrorRuntimeConfig.cs
- DataRecordInternal.cs
- PropertyNames.cs
- InternalConfigHost.cs